diff --git a/public/index.html b/public/index.html
index 6c56b3f4d..a5f7041f7 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1294,8 +1294,8 @@
TFS
-
-
+
+
@@ -1477,8 +1477,8 @@
Exponent
-
-
+
+
diff --git a/public/script.js b/public/script.js
index 999d15078..1029b53b6 100644
--- a/public/script.js
+++ b/public/script.js
@@ -606,7 +606,6 @@ export const printCharactersDebounced = debounce(() => { printCharacters(false);
export const system_message_types = {
HELP: 'help',
WELCOME: 'welcome',
- GROUP: 'group',
EMPTY: 'empty',
GENERIC: 'generic',
NARRATOR: 'narrator',
@@ -699,14 +698,6 @@ async function getSystemMessages() {
uses_system_ui: true,
mes: await renderTemplateAsync('welcome', { displayVersion }),
},
- group: {
- name: systemUserName,
- force_avatar: system_avatar,
- is_user: false,
- is_system: true,
- is_group: true,
- mes: 'Group chat created. Say \'Hi\' to lovely people!',
- },
empty: {
name: systemUserName,
force_avatar: system_avatar,
diff --git a/public/scripts/bookmarks.js b/public/scripts/bookmarks.js
index 83a2b16d4..61baf34a4 100644
--- a/public/scripts/bookmarks.js
+++ b/public/scripts/bookmarks.js
@@ -318,16 +318,6 @@ export async function convertSoloToGroupChat() {
const groupChat = chat.slice();
const genIdFirst = Date.now();
- // Add something if the chat is empty
- if (groupChat.length === 0) {
- const newMessage = {
- ...system_messages[system_message_types.GROUP],
- send_date: getMessageTimeStamp(),
- extra: { type: system_message_types.GROUP },
- };
- groupChat.push(newMessage);
- }
-
for (let index = 0; index < groupChat.length; index++) {
const message = groupChat[index];
diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js
index a197e3a41..773990fab 100644
--- a/public/scripts/extensions.js
+++ b/public/scripts/extensions.js
@@ -7,7 +7,7 @@ import { renderTemplate, renderTemplateAsync } from './templates.js';
import { delay, isSubsetOf, sanitizeSelector, setValueByPath } from './utils.js';
import { getContext } from './st-context.js';
import { isAdmin } from './user.js';
-import { t } from './i18n.js';
+import { addLocaleData, getCurrentLocale, t } from './i18n.js';
import { debounce_timeout } from './constants.js';
import { accountStorage } from './util/AccountStorage.js';
@@ -384,7 +384,7 @@ async function activateExtensions() {
if (meetsModuleRequirements && !isDisabled) {
try {
console.debug('Activating extension', name);
- const promise = Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]);
+ const promise = addExtensionLocale(name, manifest).finally(() => Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]));
await promise
.then(() => activeExtensions.add(name))
.catch(err => console.log('Could not activate extension', name, err));
@@ -576,6 +576,42 @@ function addExtensionScript(name, manifest) {
});
}
+/**
+ * Adds a localization data for an extension.
+ * @param {string} name Extension name
+ * @param {object} manifest Manifest object
+ */
+function addExtensionLocale(name, manifest) {
+ // No i18n data in the manifest
+ if (!manifest.i18n || typeof manifest.i18n !== 'object') {
+ return Promise.resolve();
+ }
+
+ const currentLocale = getCurrentLocale();
+ const localeFile = manifest.i18n[currentLocale];
+
+ // Manifest doesn't provide a locale file for the current locale
+ if (!localeFile) {
+ return Promise.resolve();
+ }
+
+ return fetch(`/scripts/extensions/${name}/${localeFile}`)
+ .then(async response => {
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+ }
+
+ const data = await response.json();
+
+ if (data && typeof data === 'object') {
+ addLocaleData(currentLocale, data);
+ }
+ })
+ .catch(err => {
+ console.log('Could not load extension locale data for ' + name, err);
+ });
+}
+
/**
* Generates HTML string for displaying an extension in the UI.
*
diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js
index f191ff983..d1431d74f 100644
--- a/public/scripts/group-chats.js
+++ b/public/scripts/group-chats.js
@@ -228,9 +228,6 @@ export async function getGroupChat(groupId, reload = false) {
chat.splice(0, chat.length, ...data);
await printMessages();
} else {
- sendSystemMessage(system_message_types.GROUP, '', { isSmallSys: true });
- await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
- await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1));
if (group && Array.isArray(group.members)) {
for (let member of group.members) {
const character = characters.find(x => x.avatar === member || x.name === member);
diff --git a/public/scripts/i18n.js b/public/scripts/i18n.js
index 39dccda4d..9dd213b04 100644
--- a/public/scripts/i18n.js
+++ b/public/scripts/i18n.js
@@ -11,6 +11,30 @@ var localeData;
export const getCurrentLocale = () => localeFile;
+/**
+ * Adds additional localization data to the current locale file.
+ * @param {string} localeId Locale ID (e.g. 'fr-fr' or 'zh-cn')
+ * @param {Record} data Localization data to add
+ */
+export function addLocaleData(localeId, data) {
+ if (!localeData) {
+ console.warn('Localization data not loaded yet. Additional data will not be added.');
+ return;
+ }
+
+ if (localeId !== localeFile) {
+ console.debug('Ignoring addLocaleData call for different locale', localeId);
+ return;
+ }
+
+ for (const [key, value] of Object.entries(data)) {
+ // Overrides for default locale data are not allowed
+ if (!Object.hasOwn(localeData, key)) {
+ localeData[key] = value;
+ }
+ }
+}
+
/**
* An observer that will check if any new i18n elements are added to the document
* @type {MutationObserver}
diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js
index ba8669a71..b06b2dacd 100644
--- a/public/scripts/power-user.js
+++ b/public/scripts/power-user.js
@@ -644,7 +644,6 @@ async function CreateZenSliders(elmnt) {
}
if (sliderID == 'min_temp_textgenerationwebui' ||
sliderID == 'max_temp_textgenerationwebui' ||
- sliderID == 'dynatemp_exponent_textgenerationwebui' ||
sliderID == 'smoothing_curve_textgenerationwebui' ||
sliderID == 'smoothing_factor_textgenerationwebui' ||
sliderID == 'dry_multiplier_textgenerationwebui' ||
diff --git a/public/scripts/st-context.js b/public/scripts/st-context.js
index 2393a4800..ad7021622 100644
--- a/public/scripts/st-context.js
+++ b/public/scripts/st-context.js
@@ -54,7 +54,7 @@ import {
writeExtensionField,
} from './extensions.js';
import { groups, openGroupChat, selected_group } from './group-chats.js';
-import { t, translate } from './i18n.js';
+import { addLocaleData, getCurrentLocale, t, translate } from './i18n.js';
import { hideLoader, showLoader } from './loader.js';
import { MacrosParser } from './macros.js';
import { getChatCompletionModel, oai_settings } from './openai.js';
@@ -165,6 +165,8 @@ export function getContext() {
isMobile,
t,
translate,
+ getCurrentLocale,
+ addLocaleData,
tags,
tagMap: tag_map,
menuType: menu_type,
diff --git a/src/endpoints/extensions.js b/src/endpoints/extensions.js
index 95e5e7eba..542be64ba 100644
--- a/src/endpoints/extensions.js
+++ b/src/endpoints/extensions.js
@@ -323,7 +323,7 @@ router.get('/discover', jsonParser, function (request, response) {
// Combine all extensions
const allExtensions = [...builtInExtensions, ...userExtensions, ...globalExtensions];
- console.info('Extensions available for', request.user.profile.handle, allExtensions);
+ console.debug('Extensions available for', request.user.profile.handle, allExtensions);
return response.send(allExtensions);
});