summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsana2dang <sana2dang@naver.com>2026-03-12 16:07:04 +0900
committersana2dang <sana2dang@naver.com>2026-03-12 16:07:04 +0900
commitf4e2769d069fcf87da5ec7f39e0d372903bdb810 (patch)
tree373af291caf42538b20d14a0ff943ed6c83e670c /src
parent97a70cd68813f2174fe145ee79784e509d11a742 (diff)
downloaddecky-lsfg-vk-f4e2769d069fcf87da5ec7f39e0d372903bdb810.tar.gz
decky-lsfg-vk-f4e2769d069fcf87da5ec7f39e0d372903bdb810.zip
Apply i18n to all UI components with ko/ja translations
- Add src/i18n/i18n.ts translation engine (ported from SimpleDeckyTDP) - Add defaults/i18n/{ko,ja,template}.json with 80 translation keys - Add defaults/i18n/language_metadata.json and steam_language_map.json - Add scripts/build_i18n_json.sh to generate languages.json - Apply t() to all 10 components: Content, ConfigurationSection, FlatpaksModal, FpsMultiplierControl, InstallationButton, NerdStuffModal, ProfileManagement, SmartClipboardButton, FgmodClipboardButton, UsageInstructions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src')
-rw-r--r--src/components/ConfigurationSection.tsx53
-rw-r--r--src/components/Content.tsx7
-rw-r--r--src/components/FgmodClipboardButton.tsx3
-rw-r--r--src/components/FlatpaksModal.tsx91
-rw-r--r--src/components/FpsMultiplierControl.tsx3
-rw-r--r--src/components/InstallationButton.tsx9
-rw-r--r--src/components/NerdStuffModal.tsx31
-rw-r--r--src/components/ProfileManagement.tsx41
-rw-r--r--src/components/SmartClipboardButton.tsx3
-rw-r--r--src/components/UsageInstructions.tsx7
-rw-r--r--src/i18n/i18n.ts65
-rw-r--r--src/i18n/languages.json351
12 files changed, 545 insertions, 119 deletions
diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx
index 0734297..c988f9f 100644
--- a/src/components/ConfigurationSection.tsx
+++ b/src/components/ConfigurationSection.tsx
@@ -7,6 +7,7 @@ import {
EXPERIMENTAL_PRESENT_MODE, DXVK_FRAME_RATE, DISABLE_STEAMDECK_MODE,
MANGOHUD_WORKAROUND, DISABLE_VKBASALT, FORCE_ENABLE_VKBASALT, ENABLE_WSI, ENABLE_ZINK
} from "../config/generatedConfigSchema";
+import t from '../i18n/i18n';
interface ConfigurationSectionProps {
config: ConfigurationData;
@@ -84,7 +85,7 @@ export function ConfigurationSection({
color: "white"
}}
>
- Config
+ {t('CONFIG_SECTION_TITLE', 'Config')}
</div>
</PanelSectionRow>
@@ -115,8 +116,8 @@ export function ConfigurationSection({
<>
<PanelSectionRow>
<SliderField
- label={`Flow Scale (${Math.round(config.flow_scale * 100)}%)`}
- description="Lowers internal motion estimation resolution, improving performance slightly"
+ label={`${t('CONFIG_FLOW_SCALE', 'Flow Scale')} (${Math.round(config.flow_scale * 100)}%)`}
+ description={t('CONFIG_FLOW_SCALE_DESC', 'Lowers internal motion estimation resolution, improving performance slightly')}
value={config.flow_scale}
min={0.25}
max={1.0}
@@ -127,8 +128,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<SliderField
- label={`Base FPS Cap${config.dxvk_frame_rate > 0 ? ` (${config.dxvk_frame_rate} FPS)` : " (Off)"}`}
- description="Base framerate cap for DirectX games, before frame multiplier. (Requires game restart to apply)"
+ label={`${t('CONFIG_BASE_FPS_CAP', 'Base FPS Cap')}${config.dxvk_frame_rate > 0 ? ` (${config.dxvk_frame_rate} FPS)` : ` (${t('CONFIG_BASE_FPS_CAP_OFF', 'Off')})`}`}
+ description={t('CONFIG_BASE_FPS_CAP_DESC', 'Base framerate cap for DirectX games, before frame multiplier. (Requires game restart to apply)')}
value={config.dxvk_frame_rate}
min={0}
max={60}
@@ -139,8 +140,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label={`Present Mode (${(config.experimental_present_mode || "fifo") === "fifo" ? "FIFO - VSync" : "Mailbox"})`}
- description="Toggle between FIFO - VSync (default) and Mailbox presentation modes for better performance or compatibility"
+ label={`${t('CONFIG_PRESENT_MODE', 'Present Mode')} (${(config.experimental_present_mode || "fifo") === "fifo" ? t('CONFIG_PRESENT_MODE_FIFO', 'FIFO - VSync') : t('CONFIG_PRESENT_MODE_MAILBOX', 'Mailbox')})`}
+ description={t('CONFIG_PRESENT_MODE_DESC', 'Toggle between FIFO - VSync (default) and Mailbox presentation modes for better performance or compatibility')}
checked={(config.experimental_present_mode || "fifo") === "fifo"}
onChange={(value) => onConfigChange(EXPERIMENTAL_PRESENT_MODE, value ? "fifo" : "mailbox")}
/>
@@ -148,8 +149,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label="Performance Mode"
- description="Uses a lighter model for FG (Recommended for most games)"
+ label={t('CONFIG_PERFORMANCE_MODE', 'Performance Mode')}
+ description={t('CONFIG_PERFORMANCE_MODE_DESC', 'Uses a lighter model for FG (Recommended for most games)')}
checked={config.performance_mode}
onChange={(value) => onConfigChange(PERFORMANCE_MODE, value)}
/>
@@ -157,8 +158,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label="HDR Mode"
- description="Enables HDR mode (only for games that support HDR)"
+ label={t('CONFIG_HDR_MODE', 'HDR Mode')}
+ description={t('CONFIG_HDR_MODE_DESC', 'Enables HDR mode (only for games that support HDR)')}
checked={config.hdr_mode}
onChange={(value) => onConfigChange(HDR_MODE, value)}
/>
@@ -179,7 +180,7 @@ export function ConfigurationSection({
color: "white"
}}
>
- Workarounds
+ {t('CONFIG_WORKAROUNDS_TITLE', 'Workarounds')}
</div>
</PanelSectionRow>
@@ -210,8 +211,8 @@ export function ConfigurationSection({
<>
<PanelSectionRow>
<ToggleField
- label="Enable WSI"
- description="Re-Enable Gamescope WSI Layer. Requires game restart to apply."
+ label={t('CONFIG_ENABLE_WSI', 'Enable WSI')}
+ description={t('CONFIG_ENABLE_WSI_DESC', 'Re-Enable Gamescope WSI Layer. Requires game restart to apply.')}
checked={config.enable_wsi}
onChange={(value) => onConfigChange(ENABLE_WSI, value)}
/>
@@ -219,8 +220,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label="Enable WOW64 for 32-bit games"
- description="Enables PROTON_USE_WOW64=1 for 32-bit games (Use with ProtonGE to fix crashing)"
+ label={t('CONFIG_ENABLE_WOW64', 'Enable WOW64 for 32-bit games')}
+ description={t('CONFIG_ENABLE_WOW64_DESC', 'Enables PROTON_USE_WOW64=1 for 32-bit games (Use with ProtonGE to fix crashing)')}
checked={config.enable_wow64}
onChange={(value) => onConfigChange('enable_wow64', value)}
/>
@@ -228,8 +229,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label="Disable Steam Deck Mode"
- description="Disables Steam Deck mode (Unlocks hidden settings in some games)"
+ label={t('CONFIG_DISABLE_STEAMDECK_MODE', 'Disable Steam Deck Mode')}
+ description={t('CONFIG_DISABLE_STEAMDECK_MODE_DESC', 'Disables Steam Deck mode (Unlocks hidden settings in some games)')}
checked={config.disable_steamdeck_mode}
onChange={(value) => onConfigChange(DISABLE_STEAMDECK_MODE, value)}
/>
@@ -237,8 +238,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label="MangoHud Workaround"
- description="Enables a transparent mangohud overlay, sometimes fixes issues with 2X multiplier in game mode"
+ label={t('CONFIG_MANGOHUD_WORKAROUND', 'MangoHud Workaround')}
+ description={t('CONFIG_MANGOHUD_WORKAROUND_DESC', 'Enables a transparent mangohud overlay, sometimes fixes issues with 2X multiplier in game mode')}
checked={config.mangohud_workaround}
onChange={(value) => onConfigChange(MANGOHUD_WORKAROUND, value)}
/>
@@ -246,8 +247,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label="Disable vkBasalt"
- description="Disables vkBasalt layer which can conflict with LSFG (Reshade, some Decky plugins)"
+ label={t('CONFIG_DISABLE_VKBASALT', 'Disable vkBasalt')}
+ description={t('CONFIG_DISABLE_VKBASALT_DESC', 'Disables vkBasalt layer which can conflict with LSFG (Reshade, some Decky plugins)')}
checked={config.disable_vkbasalt}
disabled={config.force_enable_vkbasalt}
onChange={(value) => {
@@ -262,8 +263,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label="Force Enable vkBasalt"
- description="Force vkBasalt to engage to fix framepacing issues in gamemode"
+ label={t('CONFIG_FORCE_ENABLE_VKBASALT', 'Force Enable vkBasalt')}
+ description={t('CONFIG_FORCE_ENABLE_VKBASALT_DESC', 'Force vkBasalt to engage to fix framepacing issues in gamemode')}
checked={config.force_enable_vkbasalt}
disabled={config.disable_vkbasalt}
onChange={(value) => {
@@ -278,8 +279,8 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label="Enable Zink for OpenGL Games"
- description="Use Vulkan-based OpenGL implementation for OpenGL games (may cause crashes or freezes with some games)"
+ label={t('CONFIG_ENABLE_ZINK', 'Enable Zink for OpenGL Games')}
+ description={t('CONFIG_ENABLE_ZINK_DESC', 'Use Vulkan-based OpenGL implementation for OpenGL games (may cause crashes or freezes with some games)')}
checked={config.enable_zink}
onChange={(value) => onConfigChange(ENABLE_ZINK, value)}
/>
diff --git a/src/components/Content.tsx b/src/components/Content.tsx
index 3dc2696..c785a50 100644
--- a/src/components/Content.tsx
+++ b/src/components/Content.tsx
@@ -14,6 +14,7 @@ import { FpsMultiplierControl } from "./FpsMultiplierControl";
import { NerdStuffModal } from "./NerdStuffModal";
import { FlatpaksModal } from "./FlatpaksModal";
import { ConfigurationData } from "../config/configSchema";
+import t from '../i18n/i18n';
export function Content() {
const {
@@ -108,7 +109,7 @@ export function Content() {
color: "white"
}}
>
- FPS Multiplier
+ {t('CONTENT_FPS_MULTIPLIER', 'FPS Multiplier')}
</div>
</PanelSectionRow>
@@ -150,7 +151,7 @@ export function Content() {
layout="below"
onClick={handleShowNerdStuff}
>
- Nerd Stuff
+ {t('CONTENT_NERD_STUFF', 'Nerd Stuff')}
</ButtonItem>
</PanelSectionRow>
@@ -159,7 +160,7 @@ export function Content() {
layout="below"
onClick={handleShowFlatpaks}
>
- Flatpak Setup
+ {t('CONTENT_FLATPAK_SETUP', 'Flatpak Setup')}
</ButtonItem>
</PanelSectionRow>
diff --git a/src/components/FgmodClipboardButton.tsx b/src/components/FgmodClipboardButton.tsx
index 6f65955..efc5490 100644
--- a/src/components/FgmodClipboardButton.tsx
+++ b/src/components/FgmodClipboardButton.tsx
@@ -4,6 +4,7 @@ import { FaClipboard, FaCheck } from "react-icons/fa";
import { checkFgmodDirectory } from "../api/lsfgApi";
import { showClipboardErrorToast } from "../utils/toastUtils";
import { copyWithVerification } from "../utils/clipboardUtils";
+import t from '../i18n/i18n';
export function FgmodClipboardButton() {
const [isLoading, setIsLoading] = useState(false);
@@ -93,7 +94,7 @@ export function FgmodClipboardButton() {
color: showSuccess ? "#4CAF50" : "inherit",
fontWeight: showSuccess ? "bold" : "normal"
}}>
- {showSuccess ? "Copied to clipboard" : isLoading ? "Copying..." : "LSFG + DeckyFG"}
+ {showSuccess ? t('CLIPBOARD_COPIED', 'Copied to clipboard') : isLoading ? t('CLIPBOARD_COPYING', 'Copying...') : t('CLIPBOARD_LSFG_FGMOD', 'LSFG + DeckyFG')}
</div>
</div>
</ButtonItem>
diff --git a/src/components/FlatpaksModal.tsx b/src/components/FlatpaksModal.tsx
index a80fff0..16d0369 100644
--- a/src/components/FlatpaksModal.tsx
+++ b/src/components/FlatpaksModal.tsx
@@ -27,6 +27,7 @@ import {
FlatpakApp,
FlatpakAppInfo
} from '../api/lsfgApi';
+import t from '../i18n/i18n';
interface FlatpaksModalProps {
closeModal?: () => void;
@@ -116,7 +117,7 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
if (loading) {
return (
<ModalRoot closeModal={closeModal}>
- <DialogHeader>Flatpak Extensions</DialogHeader>
+ <DialogHeader>{t('FLATPAK_MODAL_TITLE', 'Flatpak Extensions')}</DialogHeader>
<DialogBody>
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
<Spinner />
@@ -129,17 +130,17 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
const instructionSteps = [
{
id: 'try-first',
- title: 'Try first:',
+ title: t('FLATPAK_STEP_TRY_FIRST', 'Try first:'),
command: '~/lsfg'
},
{
id: 'try-full-path',
- title: "If that doesn't work, try full path:",
+ title: t('FLATPAK_STEP_TRY_FULL_PATH', "If that doesn't work, try full path:"),
command: '/home/(username)/lsfg'
},
{
id: 'final-result',
- title: 'Final result should look like:',
+ title: t('FLATPAK_STEP_FINAL', 'Final result should look like:'),
command: '~/lsfg "usr/bin/flatpak"'
}
];
@@ -162,20 +163,20 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
return (
<ModalRoot closeModal={closeModal}>
- <DialogHeader>Flatpak Extensions</DialogHeader>
+ <DialogHeader>{t('FLATPAK_MODAL_TITLE', 'Flatpak Extensions')}</DialogHeader>
<DialogBody>
<Focusable>
{/* Extension Status Section */}
<DialogControlsSection>
- <DialogControlsSectionHeader>Runtime Extension Installer</DialogControlsSectionHeader>
+ <DialogControlsSectionHeader>{t('FLATPAK_RUNTIME_INSTALLER', 'Runtime Extension Installer')}</DialogControlsSectionHeader>
{extensionStatus && extensionStatus.success ? (
<>
{/* 23.08 Runtime */}
<PanelSectionRow>
- <Field
- label="Runtime 23.08"
- description={extensionStatus.installed_23_08 ? "Installed" : "Not installed"}
+ <Field
+ label={t('FLATPAK_RUNTIME_23', 'Runtime 23.08')}
+ description={extensionStatus.installed_23_08 ? t('FLATPAK_INSTALLED', 'Installed') : t('FLATPAK_NOT_INSTALLED', 'Not installed')}
icon={extensionStatus.installed_23_08 ? <FaCheck style={{color: 'green'}} /> : <FaTimes style={{color: 'red'}} />}
>
<ButtonItem
@@ -187,8 +188,8 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
if (operation === 'uninstall') {
confirmOperation(
action,
- 'Uninstall Runtime Extension',
- 'Are you sure you want to uninstall the 23.08 runtime extension?'
+ t('FLATPAK_UNINSTALL_TITLE', 'Uninstall Runtime Extension'),
+ `${t('FLATPAK_UNINSTALL_CONFIRM_PREFIX', 'Are you sure you want to uninstall the')} 23.08 ${t('FLATPAK_UNINSTALL_CONFIRM_SUFFIX', 'runtime extension?')}`
);
} else {
action();
@@ -200,11 +201,11 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
<Spinner />
) : extensionStatus.installed_23_08 ? (
<>
- <FaTrash /> Uninstall
+ <FaTrash /> {t('FLATPAK_UNINSTALL_BTN', 'Uninstall')}
</>
) : (
<>
- <FaDownload /> Install
+ <FaDownload /> {t('FLATPAK_INSTALL_BTN', 'Install')}
</>
)}
</ButtonItem>
@@ -213,9 +214,9 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
{/* 24.08 Runtime */}
<PanelSectionRow>
- <Field
- label="Runtime 24.08"
- description={extensionStatus.installed_24_08 ? "Installed" : "Not installed"}
+ <Field
+ label={t('FLATPAK_RUNTIME_24', 'Runtime 24.08')}
+ description={extensionStatus.installed_24_08 ? t('FLATPAK_INSTALLED', 'Installed') : t('FLATPAK_NOT_INSTALLED', 'Not installed')}
icon={extensionStatus.installed_24_08 ? <FaCheck style={{color: 'green'}} /> : <FaTimes style={{color: 'red'}} />}
>
<ButtonItem
@@ -227,8 +228,8 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
if (operation === 'uninstall') {
confirmOperation(
action,
- 'Uninstall Runtime Extension',
- 'Are you sure you want to uninstall the 24.08 runtime extension?'
+ t('FLATPAK_UNINSTALL_TITLE', 'Uninstall Runtime Extension'),
+ `${t('FLATPAK_UNINSTALL_CONFIRM_PREFIX', 'Are you sure you want to uninstall the')} 24.08 ${t('FLATPAK_UNINSTALL_CONFIRM_SUFFIX', 'runtime extension?')}`
);
} else {
action();
@@ -240,11 +241,11 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
<Spinner />
) : extensionStatus.installed_24_08 ? (
<>
- <FaTrash /> Uninstall
+ <FaTrash /> {t('FLATPAK_UNINSTALL_BTN', 'Uninstall')}
</>
) : (
<>
- <FaDownload /> Install
+ <FaDownload /> {t('FLATPAK_INSTALL_BTN', 'Install')}
</>
)}
</ButtonItem>
@@ -253,9 +254,9 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
{/* 25.08 Runtime */}
<PanelSectionRow>
- <Field
- label="Runtime 25.08"
- description={extensionStatus.installed_25_08 ? "Installed" : "Not installed"}
+ <Field
+ label={t('FLATPAK_RUNTIME_25', 'Runtime 25.08')}
+ description={extensionStatus.installed_25_08 ? t('FLATPAK_INSTALLED', 'Installed') : t('FLATPAK_NOT_INSTALLED', 'Not installed')}
icon={extensionStatus.installed_25_08 ? <FaCheck style={{color: 'green'}} /> : <FaTimes style={{color: 'red'}} />}
>
<ButtonItem
@@ -267,8 +268,8 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
if (operation === 'uninstall') {
confirmOperation(
action,
- 'Uninstall Runtime Extension',
- 'Are you sure you want to uninstall the 25.08 runtime extension?'
+ t('FLATPAK_UNINSTALL_TITLE', 'Uninstall Runtime Extension'),
+ `${t('FLATPAK_UNINSTALL_CONFIRM_PREFIX', 'Are you sure you want to uninstall the')} 25.08 ${t('FLATPAK_UNINSTALL_CONFIRM_SUFFIX', 'runtime extension?')}`
);
} else {
action();
@@ -280,11 +281,11 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
<Spinner />
) : extensionStatus.installed_25_08 ? (
<>
- <FaTrash /> Uninstall
+ <FaTrash /> {t('FLATPAK_UNINSTALL_BTN', 'Uninstall')}
</>
) : (
<>
- <FaDownload /> Install
+ <FaDownload /> {t('FLATPAK_INSTALL_BTN', 'Install')}
</>
)}
</ButtonItem>
@@ -293,9 +294,9 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
</>
) : (
<PanelSectionRow>
- <Field
- label="Error"
- description={extensionStatus?.error || 'Failed to check extension status'}
+ <Field
+ label={t('FLATPAK_ERROR', 'Error')}
+ description={extensionStatus?.error || t('FLATPAK_ERROR_STATUS', 'Failed to check extension status')}
icon={<FaTimes style={{color: 'red'}} />}
/>
</PanelSectionRow>
@@ -304,7 +305,7 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
{/* Flatpak Apps Section */}
<DialogControlsSection>
- <DialogControlsSectionHeader>Flatpak Applications</DialogControlsSectionHeader>
+ <DialogControlsSectionHeader>{t('FLATPAK_APPS_TITLE', 'Flatpak Applications')}</DialogControlsSectionHeader>
{flatpakApps && flatpakApps.success ? (
flatpakApps.apps.length > 0 ? (
@@ -313,14 +314,14 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
const partialOverrides = app.has_filesystem_override || app.has_env_override;
let statusColor = 'red';
- let statusText = 'No overrides';
+ let statusText = t('FLATPAK_STATUS_NO_OVERRIDES', 'No overrides');
if (hasOverrides) {
statusColor = 'green';
- statusText = 'Configured';
+ statusText = t('FLATPAK_STATUS_CONFIGURED', 'Configured');
} else if (partialOverrides) {
statusColor = 'orange';
- statusText = 'Partial';
+ statusText = t('FLATPAK_STATUS_PARTIAL', 'Partial');
}
return (
@@ -341,17 +342,17 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
})
) : (
<PanelSectionRow>
- <Field
- label="No Flatpak Apps Found"
- description="No Flatpak applications are currently installed"
+ <Field
+ label={t('FLATPAK_NO_APPS', 'No Flatpak Apps Found')}
+ description={t('FLATPAK_NO_APPS_DESC', 'No Flatpak applications are currently installed')}
/>
</PanelSectionRow>
)
) : (
<PanelSectionRow>
- <Field
- label="Error"
- description={flatpakApps?.error || 'Failed to load Flatpak applications'}
+ <Field
+ label={t('FLATPAK_ERROR', 'Error')}
+ description={flatpakApps?.error || t('FLATPAK_ERROR_APPS', 'Failed to load Flatpak applications')}
icon={<FaTimes style={{color: 'red'}} />}
/>
</PanelSectionRow>
@@ -360,7 +361,7 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
{/* Steam Configuration Instructions */}
<DialogControlsSection>
- <DialogControlsSectionHeader>Steam Configuration</DialogControlsSectionHeader>
+ <DialogControlsSectionHeader>{t('FLATPAK_STEAM_CONFIG_TITLE', 'Steam Configuration')}</DialogControlsSectionHeader>
<div
style={{
padding: '12px',
@@ -372,13 +373,13 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
}}
>
<div style={{ fontWeight: 'bold', marginBottom: '8px', color: '#fff' }}>
- Configure Steam Flatpak Shortcuts
+ {t('FLATPAK_STEAM_CONFIG_HEADER', 'Configure Steam Flatpak Shortcuts')}
</div>
<div style={{ fontSize: '0.9em', lineHeight: '1.4', marginBottom: '8px' }}>
- In Steam, open your flatpak game and click the cog wheel.
+ {t('FLATPAK_STEAM_CONFIG_DESC', 'In Steam, open your flatpak game and click the cog wheel.')}
</div>
<div style={{ fontSize: '0.9em', lineHeight: '1.4', marginBottom: '12px', color: '#ffa500' }}>
- <strong>IMPORTANT:</strong> Set this in TARGET (NOT LAUNCH OPTIONS)
+ <strong>IMPORTANT:</strong> {t('FLATPAK_STEAM_CONFIG_IMPORTANT', 'Set this in TARGET (NOT LAUNCH OPTIONS)')}
</div>
{instructionSteps.map((step) => (
@@ -421,7 +422,7 @@ export const FlatpaksModal: FC<FlatpaksModalProps> = ({ closeModal }) => {
layout="below"
onClick={closeModal}
>
- Close
+ {t('FLATPAK_CLOSE', 'Close')}
</ButtonItem>
</PanelSectionRow>
</DialogControlsSection>
diff --git a/src/components/FpsMultiplierControl.tsx b/src/components/FpsMultiplierControl.tsx
index 5ac31bb..206643e 100644
--- a/src/components/FpsMultiplierControl.tsx
+++ b/src/components/FpsMultiplierControl.tsx
@@ -1,6 +1,7 @@
import { PanelSectionRow, DialogButton, Focusable } from "@decky/ui";
import { ConfigurationData } from "../config/configSchema";
import { MULTIPLIER } from "../config/generatedConfigSchema";
+import t from '../i18n/i18n';
interface FpsMultiplierControlProps {
config: ConfigurationData;
@@ -49,7 +50,7 @@ export function FpsMultiplierControl({
textAlign: "center"
}}
>
- {config.multiplier < 2 ? "OFF" : `${config.multiplier}X`}
+ {config.multiplier < 2 ? t('MULTIPLIER_OFF', 'OFF') : `${config.multiplier}X`}
</div>
<DialogButton
style={{
diff --git a/src/components/InstallationButton.tsx b/src/components/InstallationButton.tsx
index d0f2ce5..7f0ac96 100644
--- a/src/components/InstallationButton.tsx
+++ b/src/components/InstallationButton.tsx
@@ -1,5 +1,6 @@
import { ButtonItem, PanelSectionRow } from "@decky/ui";
import { FaDownload, FaTrash } from "react-icons/fa";
+import t from '../i18n/i18n';
interface InstallationButtonProps {
isInstalled: boolean;
@@ -20,7 +21,7 @@ export function InstallationButton({
if (isInstalling) {
return (
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <div>Installing...</div>
+ <div>{t('INSTALL_INSTALLING', 'Installing...')}</div>
</div>
);
}
@@ -28,7 +29,7 @@ export function InstallationButton({
if (isUninstalling) {
return (
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <div>Uninstalling...</div>
+ <div>{t('INSTALL_UNINSTALLING', 'Uninstalling...')}</div>
</div>
);
}
@@ -37,7 +38,7 @@ export function InstallationButton({
return (
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<FaTrash />
- <div>Uninstall LSFG-VK</div>
+ <div>{t('INSTALL_UNINSTALL_BTN', 'Uninstall LSFG-VK')}</div>
</div>
);
}
@@ -45,7 +46,7 @@ export function InstallationButton({
return (
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<FaDownload />
- <div>Install LSFG-VK</div>
+ <div>{t('INSTALL_INSTALL_BTN', 'Install LSFG-VK')}</div>
</div>
);
};
diff --git a/src/components/NerdStuffModal.tsx b/src/components/NerdStuffModal.tsx
index b4a79a2..8a79b67 100644
--- a/src/components/NerdStuffModal.tsx
+++ b/src/components/NerdStuffModal.tsx
@@ -8,6 +8,7 @@ import {
ButtonItem
} from "@decky/ui";
import { getDllStats, DllStatsResult, getConfigFileContent, getLaunchScriptContent, FileContentResult } from "../api/lsfgApi";
+import t from '../i18n/i18n';
interface NerdStuffModalProps {
closeModal?: () => void;
@@ -63,7 +64,7 @@ export function NerdStuffModal({ closeModal }: NerdStuffModalProps) {
return (
<ModalRoot onCancel={closeModal} onOK={closeModal}>
{loading && (
- <div>Loading information...</div>
+ <div>{t('NERD_LOADING', 'Loading information...')}</div>
)}
{error && (
@@ -79,26 +80,26 @@ export function NerdStuffModal({ closeModal }: NerdStuffModalProps) {
<div>{dllStats.error || "Failed to get DLL stats"}</div>
) : (
<div>
- <Field label="DLL Path">
+ <Field label={t('NERD_DLL_PATH', 'DLL Path')}>
<Focusable
onClick={() => dllStats.dll_path && copyToClipboard(dllStats.dll_path)}
onActivate={() => dllStats.dll_path && copyToClipboard(dllStats.dll_path)}
>
- {dllStats.dll_path || "Not available"}
+ {dllStats.dll_path || t('NERD_NOT_AVAILABLE', 'Not available')}
</Focusable>
</Field>
- <Field label="DLL SHA256 Hash">
+ <Field label={t('NERD_DLL_HASH', 'DLL SHA256 Hash')}>
<Focusable
onClick={() => dllStats.dll_sha256 && copyToClipboard(dllStats.dll_sha256)}
onActivate={() => dllStats.dll_sha256 && copyToClipboard(dllStats.dll_sha256)}
>
- {dllStats.dll_sha256 ? formatSHA256(dllStats.dll_sha256) : "Not available"}
+ {dllStats.dll_sha256 ? formatSHA256(dllStats.dll_sha256) : t('NERD_NOT_AVAILABLE', 'Not available')}
</Focusable>
</Field>
{dllStats.dll_source && (
- <Field label="Detection Source">
+ <Field label={t('NERD_DETECTION_SOURCE', 'Detection Source')}>
<div>{dllStats.dll_source}</div>
</Field>
)}
@@ -109,13 +110,13 @@ export function NerdStuffModal({ closeModal }: NerdStuffModalProps) {
{/* Launch Script Section */}
{scriptContent && (
- <Field label="Launch Script">
+ <Field label={t('NERD_LAUNCH_SCRIPT', 'Launch Script')}>
{!scriptContent.success ? (
- <div>Script not found: {scriptContent.error}</div>
+ <div>{t('NERD_SCRIPT_NOT_FOUND_PREFIX', 'Script not found:')} {scriptContent.error}</div>
) : (
<div>
<div style={{ marginBottom: "8px", fontSize: "0.9em", opacity: 0.8 }}>
- Path: {scriptContent.path}
+ {t('NERD_PATH_PREFIX', 'Path:')} {scriptContent.path}
</div>
<Focusable
onClick={() => scriptContent.content && copyToClipboard(scriptContent.content)}
@@ -130,7 +131,7 @@ export function NerdStuffModal({ closeModal }: NerdStuffModalProps) {
overflow: "auto",
maxHeight: "150px"
}}>
- {scriptContent.content || "No content"}
+ {scriptContent.content || t('NERD_NO_CONTENT', 'No content')}
</pre>
</Focusable>
</div>
@@ -140,13 +141,13 @@ export function NerdStuffModal({ closeModal }: NerdStuffModalProps) {
{/* Config File Section */}
{configContent && (
- <Field label="Configuration File">
+ <Field label={t('NERD_CONFIG_FILE', 'Configuration File')}>
{!configContent.success ? (
- <div>Config not found: {configContent.error}</div>
+ <div>{t('NERD_CONFIG_NOT_FOUND_PREFIX', 'Config not found:')} {configContent.error}</div>
) : (
<div>
<div style={{ marginBottom: "8px", fontSize: "0.9em", opacity: 0.8 }}>
- Path: {configContent.path}
+ {t('NERD_PATH_PREFIX', 'Path:')} {configContent.path}
</div>
<Focusable
onClick={() => configContent.content && copyToClipboard(configContent.content)}
@@ -160,7 +161,7 @@ export function NerdStuffModal({ closeModal }: NerdStuffModalProps) {
whiteSpace: "pre-wrap",
overflow: "auto"
}}>
- {configContent.content || "No content"}
+ {configContent.content || t('NERD_NO_CONTENT', 'No content')}
</pre>
</Focusable>
</div>
@@ -175,7 +176,7 @@ export function NerdStuffModal({ closeModal }: NerdStuffModalProps) {
layout="below"
onClick={closeModal}
>
- Close
+ {t('NERD_CLOSE', 'Close')}
</ButtonItem>
</PanelSectionRow>
</DialogControlsSection>
diff --git a/src/components/ProfileManagement.tsx b/src/components/ProfileManagement.tsx
index 73a40c7..626d54c 100644
--- a/src/components/ProfileManagement.tsx
+++ b/src/components/ProfileManagement.tsx
@@ -25,6 +25,7 @@ import {
ProfileResult
} from "../api/lsfgApi";
import { showSuccessToast, showErrorToast } from "../utils/toastUtils";
+import t from '../i18n/i18n';
const PROFILES_COLLAPSED_KEY = 'lsfg-profiles-collapsed';
@@ -63,8 +64,8 @@ function TextInputModal({
<p style={{ marginBottom: "24px" }}>{description}</p>
<div style={{ marginBottom: "24px" }}>
- <Field
- label="Name"
+ <Field
+ label={t('PROFILE_NAME_LABEL', 'Name')}
childrenLayout="below"
childrenContainerWidth="max"
>
@@ -199,10 +200,10 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa
const handleCreateProfile = () => {
showModal(
<TextInputModal
- title="Create New Profile"
- description="Enter a name for the new profile. The current profile's settings will be copied."
- okText="Create"
- cancelText="Cancel"
+ title={t('PROFILE_CREATE_TITLE', 'Create New Profile')}
+ description={t('PROFILE_CREATE_DESC', "Enter a name for the new profile. The current profile's settings will be copied.")}
+ okText={t('PROFILE_CREATE_BTN', 'Create')}
+ cancelText={t('PROFILE_CANCEL_BTN', 'Cancel')}
onOK={(name: string) => {
if (name.trim()) {
createNewProfile(name.trim());
@@ -237,16 +238,16 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa
const handleDeleteProfile = () => {
if (selectedProfile === "decky-lsfg-vk") {
- showErrorToast("Cannot delete default profile", "The default profile cannot be deleted");
+ showErrorToast(t('PROFILE_CANNOT_DELETE_TITLE', 'Cannot delete default profile'), t('PROFILE_CANNOT_DELETE_MSG', 'The default profile cannot be deleted'));
return;
}
showModal(
<ConfirmModal
- strTitle="Delete Profile"
- strDescription={`Are you sure you want to delete the profile "${selectedProfile}"? This action cannot be undone.`}
- strOKButtonText="Delete"
- strCancelButtonText="Cancel"
+ strTitle={t('PROFILE_DELETE_TITLE', 'Delete Profile')}
+ strDescription={`${t('PROFILE_DELETE_DESC_PREFIX', 'Are you sure you want to delete the profile')} "${selectedProfile}"${t('PROFILE_DELETE_DESC_SUFFIX', '? This action cannot be undone.')}`}
+ strOKButtonText={t('PROFILE_DELETE_BTN', 'Delete')}
+ strCancelButtonText={t('PROFILE_CANCEL_BTN', 'Cancel')}
onOK={() => deleteSelectedProfile()}
/>
);
@@ -284,17 +285,17 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa
const handleRenameProfile = () => {
if (selectedProfile === "decky-lsfg-vk") {
- showErrorToast("Cannot rename default profile", "The default profile cannot be renamed");
+ showErrorToast(t('PROFILE_CANNOT_RENAME_TITLE', 'Cannot rename default profile'), t('PROFILE_CANNOT_RENAME_MSG', 'The default profile cannot be renamed'));
return;
}
showModal(
<TextInputModal
- title="Rename Profile"
- description={`Enter a new name for the profile "${selectedProfile}".`}
+ title={t('PROFILE_RENAME_TITLE', 'Rename Profile')}
+ description={`${t('PROFILE_RENAME_DESC_PREFIX', 'Enter a new name for the profile')} "${selectedProfile}".`}
defaultValue={selectedProfile}
- okText="Rename"
- cancelText="Cancel"
+ okText={t('PROFILE_RENAME_BTN', 'Rename')}
+ cancelText={t('PROFILE_CANCEL_BTN', 'Cancel')}
onOK={(newName: string) => {
if (newName.trim() && newName.trim() !== selectedProfile) {
renameSelectedProfile(newName.trim());
@@ -330,11 +331,11 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa
const profileOptions: DropdownOption[] = [
...profiles.map((profile: string) => ({
data: profile,
- label: profile === "decky-lsfg-vk" ? "Default" : profile
+ label: profile === "decky-lsfg-vk" ? t('PROFILE_DEFAULT', 'Default') : profile
})),
{
data: "__NEW_PROFILE__",
- label: "New Profile"
+ label: t('PROFILE_NEW', 'New Profile')
}
];
@@ -361,7 +362,7 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa
border: "1px solid rgba(0, 255, 0, 0.3)",
fontSize: "13px"
}}>
- <strong>{mainRunningApp.display_name}</strong> running. Close game to change profile.
+ <strong>{mainRunningApp.display_name}</strong> running. {t('PROFILE_CLOSE_GAME', 'Close game to change profile.')}
</div>
</PanelSectionRow>
)}
@@ -378,7 +379,7 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa
color: "white"
}}
>
- Profile: {selectedProfile === "decky-lsfg-vk" ? "Default" : selectedProfile}
+ {t('PROFILE_SECTION_TITLE', 'Profile:')} {selectedProfile === "decky-lsfg-vk" ? t('PROFILE_DEFAULT', 'Default') : selectedProfile}
</div>
</PanelSectionRow>
diff --git a/src/components/SmartClipboardButton.tsx b/src/components/SmartClipboardButton.tsx
index c90515a..8da239a 100644
--- a/src/components/SmartClipboardButton.tsx
+++ b/src/components/SmartClipboardButton.tsx
@@ -4,6 +4,7 @@ import { FaClipboard, FaCheck } from "react-icons/fa";
import { getLaunchOption } from "../api/lsfgApi";
import { showClipboardErrorToast } from "../utils/toastUtils";
import { copyWithVerification } from "../utils/clipboardUtils";
+import t from '../i18n/i18n';
export function SmartClipboardButton() {
const [isLoading, setIsLoading] = useState(false);
@@ -74,7 +75,7 @@ export function SmartClipboardButton() {
color: showSuccess ? "#4CAF50" : "inherit",
fontWeight: showSuccess ? "bold" : "normal"
}}>
- {showSuccess ? "Copied to clipboard" : isLoading ? "Copying..." : "Copy Launch Option"}
+ {showSuccess ? t('CLIPBOARD_COPIED', 'Copied to clipboard') : isLoading ? t('CLIPBOARD_COPYING', 'Copying...') : t('CLIPBOARD_COPY_LAUNCH', 'Copy Launch Option')}
</div>
</div>
</ButtonItem>
diff --git a/src/components/UsageInstructions.tsx b/src/components/UsageInstructions.tsx
index 0c27517..6d32506 100644
--- a/src/components/UsageInstructions.tsx
+++ b/src/components/UsageInstructions.tsx
@@ -1,5 +1,6 @@
import { PanelSectionRow } from "@decky/ui";
import { ConfigurationData } from "../config/configSchema";
+import t from '../i18n/i18n';
interface UsageInstructionsProps {
config: ConfigurationData;
@@ -20,7 +21,7 @@ export function UsageInstructions({ config: _config }: UsageInstructionsProps) {
color: "white"
}}
>
- Usage Instructions
+ {t('USAGE_TITLE', 'Usage Instructions')}
</div>
</PanelSectionRow>
@@ -33,7 +34,7 @@ export function UsageInstructions({ config: _config }: UsageInstructionsProps) {
whiteSpace: "pre-wrap"
}}
>
- Click "Copy Launch Option" button, then paste it into your Steam game's launch options to enable frame generation.
+ {t('USAGE_DESC', 'Click "Copy Launch Option" button, then paste it into your Steam game\'s launch options to enable frame generation.')}
</div>
</PanelSectionRow>
@@ -65,7 +66,7 @@ export function UsageInstructions({ config: _config }: UsageInstructionsProps) {
marginTop: "8px"
}}
>
-The configuration is stored in ~/.config/lsfg-vk/conf.toml and hot-reloads while games are running.
+ {t('USAGE_CONFIG_NOTE', 'The configuration is stored in ~/.config/lsfg-vk/conf.toml and hot-reloads while games are running.')}
</div>
</PanelSectionRow>
</>
diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts
new file mode 100644
index 0000000..83ed3da
--- /dev/null
+++ b/src/i18n/i18n.ts
@@ -0,0 +1,65 @@
+// languages.json build via CI build
+// to generate for localhost/dev, run `build_i18n_json.sh` script
+import * as languages from "./languages.json";
+
+function getLangs() {
+ const langs = languages.language_metadata;
+
+ Object.keys(languages).map((lang) => {
+ if (lang === "language_metadata" || lang == "steam_language_map") {
+ return;
+ }
+ const strs = languages[lang];
+ if (lang && strs && langs[lang]?.name) {
+ langs[lang].strings = strs;
+ }
+ });
+
+ return langs;
+}
+
+export const LANGS: {
+ [key: string]: {
+ name: string;
+ strings: {
+ [key: string]: string;
+ };
+ };
+} = getLangs();
+
+let cachedLang: string | undefined;
+
+export const getCurrentLanguage = (): string => {
+ if (cachedLang) return cachedLang;
+
+ const lang = window.LocalizationManager.m_rgLocalesToUse[0];
+ cachedLang = lang;
+ return lang;
+};
+
+export const getLanguageName = (lang?: string): string => {
+ const targetLang = lang || getCurrentLanguage();
+ return LANGS[targetLang]?.name || targetLang;
+};
+
+/**
+ * Translate a key to the current language
+ *
+ * @param key - Translation key
+ * @param originalString - Original text (fallback)
+ * @returns Translated string or original text if translation not found
+ *
+ * @example
+ * t('CONTENT_FPS_MULTIPLIER', 'FPS Multiplier')
+ */
+const t = (key: string, originalString: string): string => {
+ const lang = getCurrentLanguage();
+
+ // English always returns the original text
+ if (lang === "en") return originalString;
+
+ // Return translation if exists, otherwise return original text
+ return LANGS[lang]?.strings?.[key] ?? originalString;
+};
+
+export default t;
diff --git a/src/i18n/languages.json b/src/i18n/languages.json
new file mode 100644
index 0000000..004d71c
--- /dev/null
+++ b/src/i18n/languages.json
@@ -0,0 +1,351 @@
+{
+ "ja": {
+ "CONTENT_FPS_MULTIPLIER": "FPS倍率",
+ "CONTENT_NERD_STUFF": "詳細情報",
+ "CONTENT_FLATPAK_SETUP": "Flatpak設定",
+ "MULTIPLIER_OFF": "オフ",
+ "CONFIG_SECTION_TITLE": "設定",
+ "CONFIG_WORKAROUNDS_TITLE": "互換性設定",
+ "CONFIG_FLOW_SCALE": "フロースケール",
+ "CONFIG_FLOW_SCALE_DESC": "内部モーション推定解像度を下げて、パフォーマンスをわずかに向上させます",
+ "CONFIG_BASE_FPS_CAP": "基本FPS上限",
+ "CONFIG_BASE_FPS_CAP_OFF": "オフ",
+ "CONFIG_BASE_FPS_CAP_DESC": "フレーム倍率適用前のDirectXゲームの基本フレームレート上限。(ゲームの再起動が必要)",
+ "CONFIG_PRESENT_MODE": "プレゼンテーションモード",
+ "CONFIG_PRESENT_MODE_FIFO": "FIFO - VSync",
+ "CONFIG_PRESENT_MODE_MAILBOX": "Mailbox",
+ "CONFIG_PRESENT_MODE_DESC": "FIFO - VSync(デフォルト)とMailboxプレゼンテーションモードを切り替えて、パフォーマンスまたは互換性を向上させます",
+ "CONFIG_PERFORMANCE_MODE": "パフォーマンスモード",
+ "CONFIG_PERFORMANCE_MODE_DESC": "FGに軽量なモデルを使用します(ほとんどのゲームに推奨)",
+ "CONFIG_HDR_MODE": "HDRモード",
+ "CONFIG_HDR_MODE_DESC": "HDRモードを有効化します(HDRをサポートするゲームのみ)",
+ "CONFIG_ENABLE_WSI": "WSIを有効化",
+ "CONFIG_ENABLE_WSI_DESC": "Gamescope WSIレイヤーを再有効化します。ゲームの再起動が必要。",
+ "CONFIG_ENABLE_WOW64": "32ビットゲーム用WOW64を有効化",
+ "CONFIG_ENABLE_WOW64_DESC": "32ビットゲームにPROTON_USE_WOW64=1を有効化します(ProtonGEと併用してクラッシュを修正)",
+ "CONFIG_DISABLE_STEAMDECK_MODE": "Steam Deckモードを無効化",
+ "CONFIG_DISABLE_STEAMDECK_MODE_DESC": "Steam Deckモードを無効化します(一部ゲームの隠し設定を解放)",
+ "CONFIG_MANGOHUD_WORKAROUND": "MangoHudワークアラウンド",
+ "CONFIG_MANGOHUD_WORKAROUND_DESC": "透明なMangoHudオーバーレイを有効化します。ゲームモードでの2X倍率問題を修正することがあります",
+ "CONFIG_DISABLE_VKBASALT": "vkBasaltを無効化",
+ "CONFIG_DISABLE_VKBASALT_DESC": "LSFGと競合する可能性のあるvkBasaltレイヤーを無効化します(Reshade、一部のDeckyプラグイン)",
+ "CONFIG_FORCE_ENABLE_VKBASALT": "vkBasaltを強制有効化",
+ "CONFIG_FORCE_ENABLE_VKBASALT_DESC": "ゲームモードのフレームペーシング問題を修正するためにvkBasaltを強制有効化します",
+ "CONFIG_ENABLE_ZINK": "OpenGLゲーム用Zinkを有効化",
+ "CONFIG_ENABLE_ZINK_DESC": "OpenGLゲームにVulkanベースのOpenGL実装を使用します(一部のゲームでクラッシュやフリーズが発生する場合があります)",
+ "INSTALL_INSTALLING": "インストール中...",
+ "INSTALL_UNINSTALLING": "アンインストール中...",
+ "INSTALL_UNINSTALL_BTN": "LSFG-VKをアンインストール",
+ "INSTALL_INSTALL_BTN": "LSFG-VKをインストール",
+ "FLATPAK_MODAL_TITLE": "Flatpak拡張",
+ "FLATPAK_RUNTIME_INSTALLER": "ランタイム拡張インストーラー",
+ "FLATPAK_RUNTIME_23": "ランタイム 23.08",
+ "FLATPAK_RUNTIME_24": "ランタイム 24.08",
+ "FLATPAK_RUNTIME_25": "ランタイム 25.08",
+ "FLATPAK_INSTALLED": "インストール済み",
+ "FLATPAK_NOT_INSTALLED": "未インストール",
+ "FLATPAK_UNINSTALL_TITLE": "ランタイム拡張をアンインストール",
+ "FLATPAK_UNINSTALL_CONFIRM_PREFIX": "本当に",
+ "FLATPAK_UNINSTALL_CONFIRM_SUFFIX": "ランタイム拡張をアンインストールしますか?",
+ "FLATPAK_UNINSTALL_BTN": "アンインストール",
+ "FLATPAK_INSTALL_BTN": "インストール",
+ "FLATPAK_APPS_TITLE": "Flatpakアプリケーション",
+ "FLATPAK_NO_APPS": "Flatpakアプリなし",
+ "FLATPAK_NO_APPS_DESC": "現在インストールされているFlatpakアプリケーションはありません",
+ "FLATPAK_STATUS_CONFIGURED": "設定済み",
+ "FLATPAK_STATUS_PARTIAL": "部分設定",
+ "FLATPAK_STATUS_NO_OVERRIDES": "オーバーライドなし",
+ "FLATPAK_ERROR": "エラー",
+ "FLATPAK_ERROR_STATUS": "拡張ステータスの確認に失敗しました",
+ "FLATPAK_ERROR_APPS": "Flatpakアプリケーションの読み込みに失敗しました",
+ "FLATPAK_STEAM_CONFIG_TITLE": "Steam設定",
+ "FLATPAK_STEAM_CONFIG_HEADER": "Steam Flatpakショートカットの設定",
+ "FLATPAK_STEAM_CONFIG_DESC": "Steamでflatpakゲームを開き、歯車アイコンをクリックしてください。",
+ "FLATPAK_STEAM_CONFIG_IMPORTANT": "重要: 起動オプションではなくターゲット(TARGET)に設定してください",
+ "FLATPAK_STEP_TRY_FIRST": "まず試す:",
+ "FLATPAK_STEP_TRY_FULL_PATH": "うまくいかない場合、フルパスを試す:",
+ "FLATPAK_STEP_FINAL": "最終的な結果はこのようになります:",
+ "FLATPAK_CLOSE": "閉じる",
+ "NERD_LOADING": "情報を読み込み中...",
+ "NERD_DLL_PATH": "DLLパス",
+ "NERD_NOT_AVAILABLE": "利用不可",
+ "NERD_DLL_HASH": "DLL SHA256ハッシュ",
+ "NERD_DETECTION_SOURCE": "検出ソース",
+ "NERD_LAUNCH_SCRIPT": "起動スクリプト",
+ "NERD_SCRIPT_NOT_FOUND_PREFIX": "スクリプトが見つかりません:",
+ "NERD_PATH_PREFIX": "パス:",
+ "NERD_NO_CONTENT": "コンテンツなし",
+ "NERD_CONFIG_FILE": "設定ファイル",
+ "NERD_CONFIG_NOT_FOUND_PREFIX": "設定が見つかりません:",
+ "NERD_CLOSE": "閉じる",
+ "PROFILE_CLOSE_GAME": "プロファイルを変更するにはゲームを終了してください。",
+ "PROFILE_SECTION_TITLE": "プロファイル:",
+ "PROFILE_DEFAULT": "デフォルト",
+ "PROFILE_NEW": "新しいプロファイル",
+ "PROFILE_NAME_LABEL": "名前",
+ "PROFILE_CREATE_TITLE": "新しいプロファイルを作成",
+ "PROFILE_CREATE_DESC": "新しいプロファイルの名前を入力してください。現在のプロファイルの設定がコピーされます。",
+ "PROFILE_CREATE_BTN": "作成",
+ "PROFILE_CANCEL_BTN": "キャンセル",
+ "PROFILE_RENAME_TITLE": "プロファイルの名前を変更",
+ "PROFILE_RENAME_DESC_PREFIX": "プロファイルの新しい名前を入力してください:",
+ "PROFILE_RENAME_BTN": "名前変更",
+ "PROFILE_CANNOT_DELETE_TITLE": "デフォルトプロファイルは削除できません",
+ "PROFILE_CANNOT_DELETE_MSG": "デフォルトプロファイルは削除できません",
+ "PROFILE_DELETE_TITLE": "プロファイルを削除",
+ "PROFILE_DELETE_DESC_PREFIX": "本当にこのプロファイルを削除しますか?",
+ "PROFILE_DELETE_DESC_SUFFIX": "この操作は取り消せません。",
+ "PROFILE_DELETE_BTN": "削除",
+ "PROFILE_CANNOT_RENAME_TITLE": "デフォルトプロファイルの名前は変更できません",
+ "PROFILE_CANNOT_RENAME_MSG": "デフォルトプロファイルの名前は変更できません",
+ "USAGE_TITLE": "使用方法",
+ "USAGE_DESC": "「起動オプションをコピー」ボタンをクリックし、Steamゲームの起動オプションに貼り付けてフレーム生成を有効化してください。",
+ "USAGE_CONFIG_NOTE": "設定は~/.config/lsfg-vk/conf.tomlに保存され、ゲーム実行中もホットリロードされます。",
+ "CLIPBOARD_COPIED": "クリップボードにコピーしました",
+ "CLIPBOARD_COPYING": "コピー中...",
+ "CLIPBOARD_COPY_LAUNCH": "起動オプションをコピー",
+ "CLIPBOARD_LSFG_FGMOD": "LSFG + DeckyFG"
+ },
+ "ko": {
+ "CONTENT_FPS_MULTIPLIER": "FPS 배율",
+ "CONTENT_NERD_STUFF": "상세 정보",
+ "CONTENT_FLATPAK_SETUP": "Flatpak 설정",
+ "MULTIPLIER_OFF": "끄기",
+ "CONFIG_SECTION_TITLE": "설정",
+ "CONFIG_WORKAROUNDS_TITLE": "호환성 설정",
+ "CONFIG_FLOW_SCALE": "흐름 배율",
+ "CONFIG_FLOW_SCALE_DESC": "내부 모션 추정 해상도를 낮춰 성능을 약간 향상시킵니다",
+ "CONFIG_BASE_FPS_CAP": "기본 FPS 상한",
+ "CONFIG_BASE_FPS_CAP_OFF": "끄기",
+ "CONFIG_BASE_FPS_CAP_DESC": "프레임 배율 적용 전 DirectX 게임의 기본 프레임 상한. (게임 재시작 필요)",
+ "CONFIG_PRESENT_MODE": "프레젠테이션 모드",
+ "CONFIG_PRESENT_MODE_FIFO": "FIFO - VSync",
+ "CONFIG_PRESENT_MODE_MAILBOX": "Mailbox",
+ "CONFIG_PRESENT_MODE_DESC": "FIFO - VSync(기본)와 Mailbox 프레젠테이션 모드를 전환하여 성능 또는 호환성을 개선합니다",
+ "CONFIG_PERFORMANCE_MODE": "성능 모드",
+ "CONFIG_PERFORMANCE_MODE_DESC": "FG에 더 가벼운 모델을 사용합니다 (대부분의 게임에 권장)",
+ "CONFIG_HDR_MODE": "HDR 모드",
+ "CONFIG_HDR_MODE_DESC": "HDR 모드를 활성화합니다 (HDR을 지원하는 게임에만 해당)",
+ "CONFIG_ENABLE_WSI": "WSI 활성화",
+ "CONFIG_ENABLE_WSI_DESC": "Gamescope WSI 레이어를 다시 활성화합니다. 게임 재시작 필요.",
+ "CONFIG_ENABLE_WOW64": "32비트 게임용 WOW64 활성화",
+ "CONFIG_ENABLE_WOW64_DESC": "32비트 게임에 PROTON_USE_WOW64=1을 활성화합니다 (크래시 수정을 위해 ProtonGE와 함께 사용)",
+ "CONFIG_DISABLE_STEAMDECK_MODE": "Steam Deck 모드 비활성화",
+ "CONFIG_DISABLE_STEAMDECK_MODE_DESC": "Steam Deck 모드를 비활성화합니다 (일부 게임의 숨겨진 설정 잠금 해제)",
+ "CONFIG_MANGOHUD_WORKAROUND": "MangoHud 우회",
+ "CONFIG_MANGOHUD_WORKAROUND_DESC": "투명한 MangoHud 오버레이를 활성화합니다. 게임 모드에서 2X 배율 문제를 수정하는 데 도움이 될 수 있습니다",
+ "CONFIG_DISABLE_VKBASALT": "vkBasalt 비활성화",
+ "CONFIG_DISABLE_VKBASALT_DESC": "LSFG와 충돌할 수 있는 vkBasalt 레이어를 비활성화합니다 (Reshade, 일부 Decky 플러그인)",
+ "CONFIG_FORCE_ENABLE_VKBASALT": "vkBasalt 강제 활성화",
+ "CONFIG_FORCE_ENABLE_VKBASALT_DESC": "게임 모드에서 프레임 페이싱 문제 수정을 위해 vkBasalt를 강제 활성화합니다",
+ "CONFIG_ENABLE_ZINK": "OpenGL 게임에 Zink 활성화",
+ "CONFIG_ENABLE_ZINK_DESC": "OpenGL 게임에 Vulkan 기반 OpenGL 구현을 사용합니다 (일부 게임에서 크래시나 멈춤이 발생할 수 있습니다)",
+ "INSTALL_INSTALLING": "설치 중...",
+ "INSTALL_UNINSTALLING": "제거 중...",
+ "INSTALL_UNINSTALL_BTN": "LSFG-VK 제거",
+ "INSTALL_INSTALL_BTN": "LSFG-VK 설치",
+ "FLATPAK_MODAL_TITLE": "Flatpak 확장",
+ "FLATPAK_RUNTIME_INSTALLER": "런타임 확장 설치",
+ "FLATPAK_RUNTIME_23": "런타임 23.08",
+ "FLATPAK_RUNTIME_24": "런타임 24.08",
+ "FLATPAK_RUNTIME_25": "런타임 25.08",
+ "FLATPAK_INSTALLED": "설치됨",
+ "FLATPAK_NOT_INSTALLED": "설치 안 됨",
+ "FLATPAK_UNINSTALL_TITLE": "런타임 확장 제거",
+ "FLATPAK_UNINSTALL_CONFIRM_PREFIX": "정말로",
+ "FLATPAK_UNINSTALL_CONFIRM_SUFFIX": "런타임 확장을 제거하시겠습니까?",
+ "FLATPAK_UNINSTALL_BTN": "제거",
+ "FLATPAK_INSTALL_BTN": "설치",
+ "FLATPAK_APPS_TITLE": "Flatpak 애플리케이션",
+ "FLATPAK_NO_APPS": "Flatpak 앱 없음",
+ "FLATPAK_NO_APPS_DESC": "현재 설치된 Flatpak 애플리케이션이 없습니다",
+ "FLATPAK_STATUS_CONFIGURED": "설정됨",
+ "FLATPAK_STATUS_PARTIAL": "부분 설정",
+ "FLATPAK_STATUS_NO_OVERRIDES": "오버라이드 없음",
+ "FLATPAK_ERROR": "오류",
+ "FLATPAK_ERROR_STATUS": "확장 상태 확인 실패",
+ "FLATPAK_ERROR_APPS": "Flatpak 애플리케이션 로드 실패",
+ "FLATPAK_STEAM_CONFIG_TITLE": "Steam 설정",
+ "FLATPAK_STEAM_CONFIG_HEADER": "Steam Flatpak 단축키 설정",
+ "FLATPAK_STEAM_CONFIG_DESC": "Steam에서 Flatpak 게임을 열고 톱니바퀴를 클릭하세요.",
+ "FLATPAK_STEAM_CONFIG_IMPORTANT": "중요: 실행 옵션이 아닌 대상(TARGET)에 설정하세요",
+ "FLATPAK_STEP_TRY_FIRST": "먼저 시도:",
+ "FLATPAK_STEP_TRY_FULL_PATH": "작동하지 않으면 전체 경로 시도:",
+ "FLATPAK_STEP_FINAL": "최종 결과는 다음과 같아야 합니다:",
+ "FLATPAK_CLOSE": "닫기",
+ "NERD_LOADING": "정보 불러오는 중...",
+ "NERD_DLL_PATH": "DLL 경로",
+ "NERD_NOT_AVAILABLE": "사용 불가",
+ "NERD_DLL_HASH": "DLL SHA256 해시",
+ "NERD_DETECTION_SOURCE": "감지 소스",
+ "NERD_LAUNCH_SCRIPT": "실행 스크립트",
+ "NERD_SCRIPT_NOT_FOUND_PREFIX": "스크립트 없음:",
+ "NERD_PATH_PREFIX": "경로:",
+ "NERD_NO_CONTENT": "내용 없음",
+ "NERD_CONFIG_FILE": "설정 파일",
+ "NERD_CONFIG_NOT_FOUND_PREFIX": "설정 없음:",
+ "NERD_CLOSE": "닫기",
+ "PROFILE_CLOSE_GAME": "프로필 변경을 위해 게임을 종료하세요.",
+ "PROFILE_SECTION_TITLE": "프로필:",
+ "PROFILE_DEFAULT": "기본",
+ "PROFILE_NEW": "새 프로필",
+ "PROFILE_NAME_LABEL": "이름",
+ "PROFILE_CREATE_TITLE": "새 프로필 만들기",
+ "PROFILE_CREATE_DESC": "새 프로필 이름을 입력하세요. 현재 프로필의 설정이 복사됩니다.",
+ "PROFILE_CREATE_BTN": "만들기",
+ "PROFILE_CANCEL_BTN": "취소",
+ "PROFILE_RENAME_TITLE": "프로필 이름 변경",
+ "PROFILE_RENAME_DESC_PREFIX": "프로필의 새 이름을 입력하세요:",
+ "PROFILE_RENAME_BTN": "이름 변경",
+ "PROFILE_CANNOT_DELETE_TITLE": "기본 프로필 삭제 불가",
+ "PROFILE_CANNOT_DELETE_MSG": "기본 프로필은 삭제할 수 없습니다",
+ "PROFILE_DELETE_TITLE": "프로필 삭제",
+ "PROFILE_DELETE_DESC_PREFIX": "정말로 프로필을 삭제하시겠습니까?",
+ "PROFILE_DELETE_DESC_SUFFIX": "이 작업은 취소할 수 없습니다.",
+ "PROFILE_DELETE_BTN": "삭제",
+ "PROFILE_CANNOT_RENAME_TITLE": "기본 프로필 이름 변경 불가",
+ "PROFILE_CANNOT_RENAME_MSG": "기본 프로필의 이름은 변경할 수 없습니다",
+ "USAGE_TITLE": "사용 방법",
+ "USAGE_DESC": "\"실행 옵션 복사\" 버튼을 클릭한 후, Steam 게임의 실행 옵션에 붙여넣어 프레임 생성을 활성화하세요.",
+ "USAGE_CONFIG_NOTE": "설정은 ~/.config/lsfg-vk/conf.toml에 저장되며 게임 실행 중에도 즉시 반영됩니다.",
+ "CLIPBOARD_COPIED": "클립보드에 복사됨",
+ "CLIPBOARD_COPYING": "복사 중...",
+ "CLIPBOARD_COPY_LAUNCH": "실행 옵션 복사",
+ "CLIPBOARD_LSFG_FGMOD": "LSFG + DeckyFG"
+ },
+ "language_metadata": {
+ "ko": {
+ "name": "한국어"
+ },
+ "en": {
+ "name": "English"
+ },
+ "ja": {
+ "name": "日本語"
+ },
+ "zh": {
+ "name": "中文"
+ }
+ },
+ "steam_language_map": {
+ "korean": "ko",
+ "koreana": "ko",
+ "english": "en",
+ "japanese": "ja",
+ "schinese": "zh",
+ "tchinese": "zh",
+ "spanish": "es",
+ "french": "fr",
+ "german": "de",
+ "italian": "it",
+ "portuguese": "pt",
+ "russian": "ru"
+ },
+ "template": {
+ "CONTENT_FPS_MULTIPLIER": "FPS Multiplier",
+ "CONTENT_NERD_STUFF": "Nerd Stuff",
+ "CONTENT_FLATPAK_SETUP": "Flatpak Setup",
+ "MULTIPLIER_OFF": "OFF",
+ "CONFIG_SECTION_TITLE": "Config",
+ "CONFIG_WORKAROUNDS_TITLE": "Workarounds",
+ "CONFIG_FLOW_SCALE": "Flow Scale",
+ "CONFIG_FLOW_SCALE_DESC": "Lowers internal motion estimation resolution, improving performance slightly",
+ "CONFIG_BASE_FPS_CAP": "Base FPS Cap",
+ "CONFIG_BASE_FPS_CAP_OFF": "Off",
+ "CONFIG_BASE_FPS_CAP_DESC": "Base framerate cap for DirectX games, before frame multiplier. (Requires game restart to apply)",
+ "CONFIG_PRESENT_MODE": "Present Mode",
+ "CONFIG_PRESENT_MODE_FIFO": "FIFO - VSync",
+ "CONFIG_PRESENT_MODE_MAILBOX": "Mailbox",
+ "CONFIG_PRESENT_MODE_DESC": "Toggle between FIFO - VSync (default) and Mailbox presentation modes for better performance or compatibility",
+ "CONFIG_PERFORMANCE_MODE": "Performance Mode",
+ "CONFIG_PERFORMANCE_MODE_DESC": "Uses a lighter model for FG (Recommended for most games)",
+ "CONFIG_HDR_MODE": "HDR Mode",
+ "CONFIG_HDR_MODE_DESC": "Enables HDR mode (only for games that support HDR)",
+ "CONFIG_ENABLE_WSI": "Enable WSI",
+ "CONFIG_ENABLE_WSI_DESC": "Re-Enable Gamescope WSI Layer. Requires game restart to apply.",
+ "CONFIG_ENABLE_WOW64": "Enable WOW64 for 32-bit games",
+ "CONFIG_ENABLE_WOW64_DESC": "Enables PROTON_USE_WOW64=1 for 32-bit games (Use with ProtonGE to fix crashing)",
+ "CONFIG_DISABLE_STEAMDECK_MODE": "Disable Steam Deck Mode",
+ "CONFIG_DISABLE_STEAMDECK_MODE_DESC": "Disables Steam Deck mode (Unlocks hidden settings in some games)",
+ "CONFIG_MANGOHUD_WORKAROUND": "MangoHud Workaround",
+ "CONFIG_MANGOHUD_WORKAROUND_DESC": "Enables a transparent mangohud overlay, sometimes fixes issues with 2X multiplier in game mode",
+ "CONFIG_DISABLE_VKBASALT": "Disable vkBasalt",
+ "CONFIG_DISABLE_VKBASALT_DESC": "Disables vkBasalt layer which can conflict with LSFG (Reshade, some Decky plugins)",
+ "CONFIG_FORCE_ENABLE_VKBASALT": "Force Enable vkBasalt",
+ "CONFIG_FORCE_ENABLE_VKBASALT_DESC": "Force vkBasalt to engage to fix framepacing issues in gamemode",
+ "CONFIG_ENABLE_ZINK": "Enable Zink for OpenGL Games",
+ "CONFIG_ENABLE_ZINK_DESC": "Use Vulkan-based OpenGL implementation for OpenGL games (may cause crashes or freezes with some games)",
+ "INSTALL_INSTALLING": "Installing...",
+ "INSTALL_UNINSTALLING": "Uninstalling...",
+ "INSTALL_UNINSTALL_BTN": "Uninstall LSFG-VK",
+ "INSTALL_INSTALL_BTN": "Install LSFG-VK",
+ "FLATPAK_MODAL_TITLE": "Flatpak Extensions",
+ "FLATPAK_RUNTIME_INSTALLER": "Runtime Extension Installer",
+ "FLATPAK_RUNTIME_23": "Runtime 23.08",
+ "FLATPAK_RUNTIME_24": "Runtime 24.08",
+ "FLATPAK_RUNTIME_25": "Runtime 25.08",
+ "FLATPAK_INSTALLED": "Installed",
+ "FLATPAK_NOT_INSTALLED": "Not installed",
+ "FLATPAK_UNINSTALL_TITLE": "Uninstall Runtime Extension",
+ "FLATPAK_UNINSTALL_CONFIRM_PREFIX": "Are you sure you want to uninstall the",
+ "FLATPAK_UNINSTALL_CONFIRM_SUFFIX": "runtime extension?",
+ "FLATPAK_UNINSTALL_BTN": "Uninstall",
+ "FLATPAK_INSTALL_BTN": "Install",
+ "FLATPAK_APPS_TITLE": "Flatpak Applications",
+ "FLATPAK_NO_APPS": "No Flatpak Apps Found",
+ "FLATPAK_NO_APPS_DESC": "No Flatpak applications are currently installed",
+ "FLATPAK_STATUS_CONFIGURED": "Configured",
+ "FLATPAK_STATUS_PARTIAL": "Partial",
+ "FLATPAK_STATUS_NO_OVERRIDES": "No overrides",
+ "FLATPAK_ERROR": "Error",
+ "FLATPAK_ERROR_STATUS": "Failed to check extension status",
+ "FLATPAK_ERROR_APPS": "Failed to load Flatpak applications",
+ "FLATPAK_STEAM_CONFIG_TITLE": "Steam Configuration",
+ "FLATPAK_STEAM_CONFIG_HEADER": "Configure Steam Flatpak Shortcuts",
+ "FLATPAK_STEAM_CONFIG_DESC": "In Steam, open your flatpak game and click the cog wheel.",
+ "FLATPAK_STEAM_CONFIG_IMPORTANT": "IMPORTANT: Set this in TARGET (NOT LAUNCH OPTIONS)",
+ "FLATPAK_STEP_TRY_FIRST": "Try first:",
+ "FLATPAK_STEP_TRY_FULL_PATH": "If that doesn't work, try full path:",
+ "FLATPAK_STEP_FINAL": "Final result should look like:",
+ "FLATPAK_CLOSE": "Close",
+ "NERD_LOADING": "Loading information...",
+ "NERD_DLL_PATH": "DLL Path",
+ "NERD_NOT_AVAILABLE": "Not available",
+ "NERD_DLL_HASH": "DLL SHA256 Hash",
+ "NERD_DETECTION_SOURCE": "Detection Source",
+ "NERD_LAUNCH_SCRIPT": "Launch Script",
+ "NERD_SCRIPT_NOT_FOUND_PREFIX": "Script not found:",
+ "NERD_PATH_PREFIX": "Path:",
+ "NERD_NO_CONTENT": "No content",
+ "NERD_CONFIG_FILE": "Configuration File",
+ "NERD_CONFIG_NOT_FOUND_PREFIX": "Config not found:",
+ "NERD_CLOSE": "Close",
+ "PROFILE_CLOSE_GAME": "Close game to change profile.",
+ "PROFILE_SECTION_TITLE": "Profile:",
+ "PROFILE_DEFAULT": "Default",
+ "PROFILE_NEW": "New Profile",
+ "PROFILE_NAME_LABEL": "Name",
+ "PROFILE_CREATE_TITLE": "Create New Profile",
+ "PROFILE_CREATE_DESC": "Enter a name for the new profile. The current profile's settings will be copied.",
+ "PROFILE_CREATE_BTN": "Create",
+ "PROFILE_CANCEL_BTN": "Cancel",
+ "PROFILE_RENAME_TITLE": "Rename Profile",
+ "PROFILE_RENAME_DESC_PREFIX": "Enter a new name for the profile",
+ "PROFILE_RENAME_BTN": "Rename",
+ "PROFILE_CANNOT_DELETE_TITLE": "Cannot delete default profile",
+ "PROFILE_CANNOT_DELETE_MSG": "The default profile cannot be deleted",
+ "PROFILE_DELETE_TITLE": "Delete Profile",
+ "PROFILE_DELETE_DESC_PREFIX": "Are you sure you want to delete the profile",
+ "PROFILE_DELETE_DESC_SUFFIX": "? This action cannot be undone.",
+ "PROFILE_DELETE_BTN": "Delete",
+ "PROFILE_CANNOT_RENAME_TITLE": "Cannot rename default profile",
+ "PROFILE_CANNOT_RENAME_MSG": "The default profile cannot be renamed",
+ "USAGE_TITLE": "Usage Instructions",
+ "USAGE_DESC": "Click \"Copy Launch Option\" button, then paste it into your Steam game's launch options to enable frame generation.",
+ "USAGE_CONFIG_NOTE": "The configuration is stored in ~/.config/lsfg-vk/conf.toml and hot-reloads while games are running.",
+ "CLIPBOARD_COPIED": "Copied to clipboard",
+ "CLIPBOARD_COPYING": "Copying...",
+ "CLIPBOARD_COPY_LAUNCH": "Copy Launch Option",
+ "CLIPBOARD_LSFG_FGMOD": "LSFG + DeckyFG"
+ }
+} \ No newline at end of file