[Update] UI: ローマ字/かな 表示するように。

This commit is contained in:
Sakamoto Shiina
2025-09-17 18:07:10 +09:00
parent 396d5d7d88
commit 9cf4a378f1
8 changed files with 205 additions and 22 deletions

View File

@@ -250,6 +250,13 @@ config_page:
received_message_format: received_message_format:
label: "Message Format (Speaker2Chatbox)" label: "Message Format (Speaker2Chatbox)"
desc: "Currently, it is used in Speaker2Chatbox." desc: "Currently, it is used in Speaker2Chatbox."
convert_message_to_romaji:
label: Show Romaji
desc: Supported only when Japanese is selected as the translation language. When enabled along with '{{convert_message_to_hiragana}}', romaji will be shown on mouse hover.
convert_message_to_hiragana:
label: Show Hiragana
desc: Supported only when Japanese is selected as the translation language.
hotkeys: hotkeys:
toggle_vrct_visibility: toggle_vrct_visibility:

View File

@@ -250,6 +250,12 @@ config_page:
received_message_format: received_message_format:
label: メッセージフォーマットSpeaker2Chatbox label: メッセージフォーマットSpeaker2Chatbox
desc: 今のところ、Speaker2Chatboxで送信した時の表示に使われます。 desc: 今のところ、Speaker2Chatboxで送信した時の表示に使われます。
convert_message_to_romaji:
label: ローマ字を表示
desc: 翻訳言語として日本語を選択した時のみサポート。「{{convert_message_to_hiragana}}」と同時に有効にした場合は、マウスホバーで表示されます。
convert_message_to_hiragana:
label: ひらがなを表示
desc: 翻訳言語として日本語を選択した時のみサポート。
hotkeys: hotkeys:
toggle_vrct_visibility: toggle_vrct_visibility:

View File

@@ -45,6 +45,10 @@ export const Others = () => {
<SendMessageFormatPartsContainer /> <SendMessageFormatPartsContainer />
<ReceivedMessageFormatPartsContainer /> <ReceivedMessageFormatPartsContainer />
</div> </div>
<div>
<ConvertMessageToRomajiContainer />
<ConvertMessageToHiraganaContainer />
</div>
</div> </div>
); );
}; };
@@ -202,3 +206,34 @@ const ReceivedMessageFormatPartsContainer = () => {
/> />
); );
}; };
const ConvertMessageToRomajiContainer = () => {
const { t } = useI18n();
const { currentConvertMessageToRomaji, toggleConvertMessageToRomaji } = useOthers();
return (
<CheckboxContainer
label={t("config_page.others.convert_message_to_romaji.label")}
desc={t(
"config_page.others.convert_message_to_romaji.desc",
{ convert_message_to_hiragana: t("config_page.others.convert_message_to_hiragana.label") }
)}
variable={currentConvertMessageToRomaji}
toggleFunction={toggleConvertMessageToRomaji}
/>
);
};
const ConvertMessageToHiraganaContainer = () => {
const { t } = useI18n();
const { currentConvertMessageToHiragana, toggleConvertMessageToHiragana } = useOthers();
return (
<CheckboxContainer
label={t("config_page.others.convert_message_to_hiragana.label")}
desc={t("config_page.others.convert_message_to_hiragana.desc")}
variable={currentConvertMessageToHiragana}
toggleFunction={toggleConvertMessageToHiragana}
/>
);
};

View File

@@ -39,7 +39,7 @@ export const MessageContainer = ({ messages, status, category, created_at }) =>
setIsLocked(true); setIsLocked(true);
}; };
const is_translated_exist = messages.translated?.length >= 1; const is_translation_exist = messages.translations?.length > 0;
const is_pending = status === "pending"; const is_pending = status === "pending";
const is_sent_message = category === "sent"; const is_sent_message = category === "sent";
const is_system_message = category === "system"; const is_system_message = category === "system";
@@ -69,11 +69,11 @@ export const MessageContainer = ({ messages, status, category, created_at }) =>
</div> </div>
<div className={clsx(styles.message_box, message_type_class_name)}> <div className={clsx(styles.message_box, message_type_class_name)}>
{is_system_message ? ( {is_system_message ? (
<p className={styles.message_main_system}>{messages.message}</p> <p className={styles.message_main_system}>{messages.original.message}</p>
) : is_translated_exist ? ( ) : is_translation_exist ? (
<WithTranslatedMessages messages={messages} /> <WithTranslatedMessages messages={messages} />
) : ( ) : (
<p className={styles.message_main}>{messages.original}</p> <OriginalMessage messages={messages} />
)} )}
</div> </div>
</div> </div>
@@ -88,13 +88,74 @@ export const MessageContainer = ({ messages, status, category, created_at }) =>
); );
}; };
const WithTranslatedMessages = ({ messages }) => { const MessageWithTransliteration = ({ item }) => {
const translated_data = Array.isArray(messages.translated) ? messages.translated : [messages.translated]; const renderTokenNode = (token, key) => {
const orig = token.orig ?? "";
const hira = token.hira ?? "";
const hepburn = token.hepburn ?? "";
if ((hira && orig === hira) || (hepburn && orig === hepburn) || (!hira && !hepburn)) {
return (
<span key={key} className={styles.token}>
{orig}
</span>
);
}
if (hira && hira !== orig) {
const needHepburn = hepburn && hepburn !== orig;
const titleAttr = needHepburn ? hepburn : undefined;
return (
<ruby key={key} title={titleAttr} className={styles.tokenRuby}>
{orig}
<rt>{hira}</rt>
</ruby>
);
}
if (hepburn && hepburn !== orig) {
return (
<ruby key={key} className={styles.tokenRuby}>
{orig}
<rt>{hepburn}</rt>
</ruby>
);
}
return (
<span key={key} className={styles.token}>
{orig}
</span>
);
};
if (!item.transliteration.length) {
return <p className={styles.message_main}>{item.message}</p>;
}
return (
<p className={styles.message_main}>
{item.transliteration.map((token, idx) => renderTokenNode(token, idx))}
</p>
);
};
const OriginalMessage = ({ messages }) => {
return ( return (
<> <>
<p className={styles.message_second}>{messages.original}</p> <MessageWithTransliteration item={messages.original} />
{translated_data.map((message, index) => ( </>
<p key={index} className={styles.message_main}>{message}</p> );
};
const WithTranslatedMessages = ({ messages }) => {
return (
<>
<p className={styles.message_second}>{messages.original.message}</p>
{messages.translations.map((item, idx) => (
<div key={idx}>
<MessageWithTransliteration item={item} />
</div>
))} ))}
</> </>
); );

View File

@@ -24,8 +24,8 @@ export const useMessage = () => {
status: "pending", status: "pending",
created_at: generateTimeData(), created_at: generateTimeData(),
messages: { messages: {
original: message, original: { message: message, transliteration: [] },
translated: [], translations: [],
}, },
}); });
}; };
@@ -39,20 +39,26 @@ export const useMessage = () => {
category: "system", category: "system",
status: "system", status: "system",
created_at: date, created_at: date,
messages: {message: message}, messages: {
original: { message: message, transliteration: [] },
translations: [],
},
}); });
}; };
const addSystemMessageLog_FromBackend = (payload) => { const addSystemMessageLog_FromBackend = (payload) => {
addSystemMessageLog(payload.message); addSystemMessageLog(payload.message);
}; };
const updateSentMessageLogById = (payload) => { const updateSentMessageLogById = (payload) => {
updateMessageLogs(updateItemById(payload.id, payload.translation)); updateMessageLogs(updateItemById(payload.id, payload));
}; };
const addSentMessageLog = (payload) => { const addSentMessageLog = (payload) => {
const message_object = generateMessageObject(payload, "sent"); const message_object = generateMessageObject(payload, "sent");
addMessageLogs(message_object); addMessageLogs(message_object);
}; };
const addReceivedMessageLog = (payload) => { const addReceivedMessageLog = (payload) => {
const message_object = generateMessageObject(payload, "received"); const message_object = generateMessageObject(payload, "received");
addMessageLogs(message_object); addMessageLogs(message_object);
@@ -61,6 +67,7 @@ export const useMessage = () => {
const startTyping = () => { const startTyping = () => {
asyncStdoutToPython("/run/typing_message_box"); asyncStdoutToPython("/run/typing_message_box");
}; };
const stopTyping = () => { const stopTyping = () => {
asyncStdoutToPython("/run/stop_typing_message_box"); asyncStdoutToPython("/run/stop_typing_message_box");
}; };
@@ -83,11 +90,10 @@ export const useMessage = () => {
}; };
const generateTimeData = () => { const generateTimeData = () => {
const data = new Date().toLocaleTimeString( return new Date().toLocaleTimeString(
"ja-JP", "ja-JP",
{ hour12: false, hour: "2-digit", minute: "2-digit" }, { hour12: false, hour: "2-digit", minute: "2-digit" }
); );
return data;
}; };
const generateMessageObject = (data, category) => { const generateMessageObject = (data, category) => {
@@ -97,17 +103,17 @@ const generateMessageObject = (data, category) => {
category: category, category: category,
status: "ok", status: "ok",
messages: { messages: {
original: data.message, original: data.original,
translated: data.translation, translations: data.translations ?? [],
}, },
}; };
}; };
const updateItemById = (id, translated_data) => (current_items) => { const updateItemById = (id, updated_data) => (current_items) => {
return current_items.data.map(item => { return current_items.data.map(item => {
if (item.id === id) { if (item.id === id) {
item.status = "ok"; item.status = "ok";
item.messages.translated = translated_data; if (updated_data.translations) item.messages.translations = updated_data.translations;
} }
return item; return item;
}); });

View File

@@ -9,6 +9,8 @@ import {
useStore_MessageFormat_ExampleViewFilter, useStore_MessageFormat_ExampleViewFilter,
useStore_SendMessageFormatParts, useStore_SendMessageFormatParts,
useStore_ReceivedMessageFormatParts, useStore_ReceivedMessageFormatParts,
useStore_ConvertMessageToRomaji,
useStore_ConvertMessageToHiragana,
} from "@store"; } from "@store";
import { useStdoutToPython } from "@useStdoutToPython"; import { useStdoutToPython } from "@useStdoutToPython";
import { useNotificationStatus } from "@logics_common"; import { useNotificationStatus } from "@logics_common";
@@ -39,6 +41,11 @@ export const useOthers = () => {
// Received // Received
const { currentReceivedMessageFormatParts, updateReceivedMessageFormatParts, pendingReceivedMessageFormatParts } = useStore_ReceivedMessageFormatParts(); const { currentReceivedMessageFormatParts, updateReceivedMessageFormatParts, pendingReceivedMessageFormatParts } = useStore_ReceivedMessageFormatParts();
// Convert Message To Romaji
const { currentConvertMessageToRomaji, updateConvertMessageToRomaji, pendingConvertMessageToRomaji } = useStore_ConvertMessageToRomaji();
// Convert Message To Hiragana
const { currentConvertMessageToHiragana, updateConvertMessageToHiragana, pendingConvertMessageToHiragana } = useStore_ConvertMessageToHiragana();
const { showNotification_SaveSuccess } = useNotificationStatus(); const { showNotification_SaveSuccess } = useNotificationStatus();
// Auto Clear Message Input Box // Auto Clear Message Input Box
@@ -233,6 +240,45 @@ export const useOthers = () => {
}); });
}; };
// Convert Message To Romaji
const getConvertMessageToRomaji = () => {
pendingConvertMessageToRomaji();
asyncStdoutToPython("/get/data/convert_message_to_romaji");
};
const toggleConvertMessageToRomaji = () => {
pendingConvertMessageToRomaji();
if (currentConvertMessageToRomaji.data) {
asyncStdoutToPython("/set/disable/convert_message_to_romaji");
} else {
asyncStdoutToPython("/set/enable/convert_message_to_romaji");
}
};
const setSuccessConvertMessageToRomaji = (enabled) => {
updateConvertMessageToRomaji(enabled);
showNotification_SaveSuccess();
};
// Convert Message To Hiragana
const getConvertMessageToHiragana = () => {
pendingConvertMessageToHiragana();
asyncStdoutToPython("/get/data/convert_message_to_hiragana");
};
const toggleConvertMessageToHiragana = () => {
pendingConvertMessageToHiragana();
if (currentConvertMessageToHiragana.data) {
asyncStdoutToPython("/set/disable/convert_message_to_hiragana");
} else {
asyncStdoutToPython("/set/enable/convert_message_to_hiragana");
}
};
const setSuccessConvertMessageToHiragana = (enabled) => {
updateConvertMessageToHiragana(enabled);
showNotification_SaveSuccess();
};
return { return {
// Auto Clear Message Input Box // Auto Clear Message Input Box
@@ -303,5 +349,19 @@ export const useOthers = () => {
getReceivedMessageFormatParts, getReceivedMessageFormatParts,
setReceivedMessageFormatParts, setReceivedMessageFormatParts,
setSuccessReceivedMessageFormatParts, setSuccessReceivedMessageFormatParts,
// Convert Message To Romaji
currentConvertMessageToRomaji,
getConvertMessageToRomaji,
toggleConvertMessageToRomaji,
updateConvertMessageToRomaji,
setSuccessConvertMessageToRomaji,
// Convert Message To Hiragana
currentConvertMessageToHiragana,
getConvertMessageToHiragana,
toggleConvertMessageToHiragana,
updateConvertMessageToHiragana,
setSuccessConvertMessageToHiragana,
}; };
}; };

View File

@@ -292,6 +292,14 @@ export const ROUTE_META_LIST = [
{ endpoint: "/get/data/received_message_format_parts", ns: configs, hook_name: "useOthers", method_name: "updateReceivedMessageFormatParts" }, { endpoint: "/get/data/received_message_format_parts", ns: configs, hook_name: "useOthers", method_name: "updateReceivedMessageFormatParts" },
{ endpoint: "/set/data/received_message_format_parts", ns: configs, hook_name: "useOthers", method_name: "setSuccessReceivedMessageFormatParts" }, { endpoint: "/set/data/received_message_format_parts", ns: configs, hook_name: "useOthers", method_name: "setSuccessReceivedMessageFormatParts" },
{ endpoint: "/get/data/convert_message_to_romaji", ns: configs, hook_name: "useOthers", method_name: "updateConvertMessageToRomaji" },
{ endpoint: "/set/enable/convert_message_to_romaji", ns: configs, hook_name: "useOthers", method_name: "setSuccessConvertMessageToRomaji" },
{ endpoint: "/set/disable/convert_message_to_romaji", ns: configs, hook_name: "useOthers", method_name: "setSuccessConvertMessageToRomaji" },
{ endpoint: "/get/data/convert_message_to_hiragana", ns: configs, hook_name: "useOthers", method_name: "updateConvertMessageToHiragana" },
{ endpoint: "/set/enable/convert_message_to_hiragana", ns: configs, hook_name: "useOthers", method_name: "setSuccessConvertMessageToHiragana" },
{ endpoint: "/set/disable/convert_message_to_hiragana", ns: configs, hook_name: "useOthers", method_name: "setSuccessConvertMessageToHiragana" },
// Hotkeys // Hotkeys
{ endpoint: "/get/data/hotkeys", ns: configs, hook_name: "useHotkeys", method_name: "updateHotkeys" }, { endpoint: "/get/data/hotkeys", ns: configs, hook_name: "useHotkeys", method_name: "updateHotkeys" },
{ endpoint: "/set/data/hotkeys", ns: configs, hook_name: "useHotkeys", method_name: "setSuccessHotkeys" }, { endpoint: "/set/data/hotkeys", ns: configs, hook_name: "useHotkeys", method_name: "setSuccessHotkeys" },
@@ -323,8 +331,6 @@ export const ROUTE_META_LIST = [
{ endpoint: "/get/data/mic_no_speech_prob", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet { endpoint: "/get/data/mic_no_speech_prob", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet
{ endpoint: "/get/data/speaker_avg_logprob", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet { endpoint: "/get/data/speaker_avg_logprob", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet
{ endpoint: "/get/data/speaker_no_speech_prob", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet { endpoint: "/get/data/speaker_no_speech_prob", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet
{ endpoint: "/get/data/convert_message_to_romaji", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet
{ endpoint: "/get/data/convert_message_to_hiragana", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet
{ endpoint: "/get/data/transcription_engines", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet. (if ai_models has not been detected, this will be blank array[]. if the ai_models are ok but just network has not connected, it'l be only ["Whisper"]) { endpoint: "/get/data/transcription_engines", ns: null, hook_name: null, method_name: null }, // Not implemented on UI yet. (if ai_models has not been detected, this will be blank array[]. if the ai_models are ok but just network has not connected, it'l be only ["Whisper"])
]; ];

View File

@@ -309,6 +309,8 @@ export const { atomInstance: Atom_ReceivedMessageFormatParts, useHook: useStore_
}, },
translation_first: false, translation_first: false,
}, "ReceivedMessageFormatParts"); }, "ReceivedMessageFormatParts");
export const { atomInstance: Atom_ConvertMessageToRomaji, useHook: useStore_ConvertMessageToRomaji } = createAtomWithHook(false, "ConvertMessageToRomaji");
export const { atomInstance: Atom_ConvertMessageToHiragana, useHook: useStore_ConvertMessageToHiragana } = createAtomWithHook(false, "ConvertMessageToHiragana");
// Hotkeys // Hotkeys