diff --git a/static/js/components/Header.js b/static/js/components/Header.js
index c6c63185..ab0e940d 100644
--- a/static/js/components/Header.js
+++ b/static/js/components/Header.js
@@ -59,7 +59,7 @@ export class HeaderManager {
if (typeof toggleTheme === 'function') {
const newTheme = toggleTheme();
// 使用i18nHelpers更新themeToggle的title
- await this.updateThemeTooltip(themeToggle, newTheme);
+ this.updateThemeTooltip(themeToggle, newTheme);
}
});
}
@@ -124,7 +124,7 @@ export class HeaderManager {
this.updateHeaderForPage();
}
- async updateHeaderForPage() {
+ updateHeaderForPage() {
const headerSearch = document.getElementById('headerSearch');
const searchInput = headerSearch?.querySelector('#searchInput');
const searchButtons = headerSearch?.querySelectorAll('button');
@@ -134,22 +134,22 @@ export class HeaderManager {
headerSearch.classList.add('disabled');
if (searchInput) {
searchInput.disabled = true;
- // 使用i18nHelpers更新placeholder
- await updateElementAttribute(searchInput, 'placeholder', 'header.search.notAvailable', {}, 'Search not available on statistics page');
+ // Use i18nHelpers to update placeholder
+ updateElementAttribute(searchInput, 'placeholder', 'header.search.notAvailable', {}, 'Search not available on statistics page');
}
searchButtons?.forEach(btn => btn.disabled = true);
} else if (headerSearch) {
headerSearch.classList.remove('disabled');
if (searchInput) {
searchInput.disabled = false;
- // 使用i18nHelpers更新placeholder
- await updateElementAttribute(searchInput, 'placeholder', placeholderKey, {}, '');
+ // Use i18nHelpers to update placeholder
+ updateElementAttribute(searchInput, 'placeholder', placeholderKey, {}, '');
}
searchButtons?.forEach(btn => btn.disabled = false);
}
}
- async updateThemeTooltip(themeToggle, currentTheme) {
+ updateThemeTooltip(themeToggle, currentTheme) {
if (!themeToggle) return;
let key;
if (currentTheme === 'light') {
@@ -159,7 +159,7 @@ export class HeaderManager {
} else {
key = 'header.theme.toggle';
}
- // 使用i18nHelpers更新title
- await updateElementAttribute(themeToggle, 'title', key, {}, '');
+ // Use i18nHelpers to update title
+ updateElementAttribute(themeToggle, 'title', key, {}, '');
}
}
diff --git a/static/js/components/shared/ModelCard.js b/static/js/components/shared/ModelCard.js
index da245bcb..c0951eac 100644
--- a/static/js/components/shared/ModelCard.js
+++ b/static/js/components/shared/ModelCard.js
@@ -143,15 +143,15 @@ async function toggleFavorite(card) {
});
if (newFavoriteState) {
- const addedText = await safeTranslate('modelCard.favorites.added', {}, 'Added to favorites');
+ const addedText = safeTranslate('modelCard.favorites.added', {}, 'Added to favorites');
showToast(addedText, 'success');
} else {
- const removedText = await safeTranslate('modelCard.favorites.removed', {}, 'Removed from favorites');
+ const removedText = safeTranslate('modelCard.favorites.removed', {}, 'Removed from favorites');
showToast(removedText, 'success');
}
} catch (error) {
console.error('Failed to update favorite status:', error);
- const errorText = await safeTranslate('modelCard.favorites.updateFailed', {}, 'Failed to update favorite status');
+ const errorText = safeTranslate('modelCard.favorites.updateFailed', {}, 'Failed to update favorite status');
showToast(errorText, 'error');
}
}
@@ -164,8 +164,8 @@ function handleSendToWorkflow(card, replaceMode, modelType) {
sendLoraToWorkflow(loraSyntax, replaceMode, 'lora');
} else {
// Checkpoint send functionality - to be implemented
- safeTranslate('modelCard.sendToWorkflow.checkpointNotImplemented', {}, 'Send checkpoint to workflow - feature to be implemented')
- .then(text => showToast(text, 'info'));
+ const text = safeTranslate('modelCard.sendToWorkflow.checkpointNotImplemented', {}, 'Send checkpoint to workflow - feature to be implemented');
+ showToast(text, 'info');
}
}
@@ -200,8 +200,8 @@ async function handleExampleImagesAccess(card, modelType) {
}
} catch (error) {
console.error('Error checking for example images:', error);
- safeTranslate('modelCard.exampleImages.checkError', {}, 'Error checking for example images')
- .then(text => showToast(text, 'error'));
+ const text = safeTranslate('modelCard.exampleImages.checkError', {}, 'Error checking for example images');
+ showToast(text, 'error');
}
}
@@ -283,8 +283,8 @@ function showExampleAccessModal(card, modelType) {
// Get the model hash
const modelHash = card.dataset.sha256;
if (!modelHash) {
- safeTranslate('modelCard.exampleImages.missingHash', {}, 'Missing model hash information.')
- .then(text => showToast(text, 'error'));
+ const text = safeTranslate('modelCard.exampleImages.missingHash', {}, 'Missing model hash information.');
+ showToast(text, 'error');
return;
}
diff --git a/static/js/components/shared/ModelDescription.js b/static/js/components/shared/ModelDescription.js
index fab3f705..3299e75d 100644
--- a/static/js/components/shared/ModelDescription.js
+++ b/static/js/components/shared/ModelDescription.js
@@ -63,7 +63,7 @@ async function loadModelDescription() {
const description = await getModelApiClient().fetchModelDescription(filePath);
// Update content
- const noDescriptionText = await safeTranslate('modals.model.description.noDescription', {}, 'No model description available');
+ const noDescriptionText = safeTranslate('modals.model.description.noDescription', {}, 'No model description available');
descriptionContent.innerHTML = description || `
${noDescriptionText}
`;
descriptionContent.dataset.loaded = 'true';
@@ -72,7 +72,7 @@ async function loadModelDescription() {
} catch (error) {
console.error('Error loading model description:', error);
- const failedText = await safeTranslate('modals.model.description.failedToLoad', {}, 'Failed to load model description');
+ const failedText = safeTranslate('modals.model.description.failedToLoad', {}, 'Failed to load model description');
descriptionContent.innerHTML = `${failedText}
`;
} finally {
// Hide loading state
@@ -96,7 +96,7 @@ export async function setupModelDescriptionEditing(filePath) {
editBtn = document.createElement('button');
editBtn.className = 'edit-model-description-btn';
// Set title using i18n
- const editTitle = await safeTranslate('modals.model.description.editTitle', {}, 'Edit model description');
+ const editTitle = safeTranslate('modals.model.description.editTitle', {}, 'Edit model description');
editBtn.title = editTitle;
editBtn.innerHTML = '';
descContainer.insertBefore(editBtn, descContent);
@@ -154,7 +154,7 @@ export async function setupModelDescriptionEditing(filePath) {
}
if (!newValue) {
this.innerHTML = originalValue;
- const emptyErrorText = await safeTranslate('modals.model.description.validation.cannotBeEmpty', {}, 'Description cannot be empty');
+ const emptyErrorText = safeTranslate('modals.model.description.validation.cannotBeEmpty', {}, 'Description cannot be empty');
showToast(emptyErrorText, 'error');
exitEditMode();
return;
@@ -163,11 +163,11 @@ export async function setupModelDescriptionEditing(filePath) {
// Save to backend
const { getModelApiClient } = await import('../../api/modelApiFactory.js');
await getModelApiClient().saveModelMetadata(filePath, { modelDescription: newValue });
- const successText = await safeTranslate('modals.model.description.messages.updated', {}, 'Model description updated');
+ const successText = safeTranslate('modals.model.description.messages.updated', {}, 'Model description updated');
showToast(successText, 'success');
} catch (err) {
this.innerHTML = originalValue;
- const errorText = await safeTranslate('modals.model.description.messages.updateFailed', {}, 'Failed to update model description');
+ const errorText = safeTranslate('modals.model.description.messages.updateFailed', {}, 'Failed to update model description');
showToast(errorText, 'error');
} finally {
exitEditMode();
diff --git a/static/js/components/shared/ModelMetadata.js b/static/js/components/shared/ModelMetadata.js
index c3fb0d82..05293f25 100644
--- a/static/js/components/shared/ModelMetadata.js
+++ b/static/js/components/shared/ModelMetadata.js
@@ -83,8 +83,8 @@ export function setupModelNameEditing(filePath) {
sel.removeAllRanges();
sel.addRange(range);
- safeTranslate('modelMetadata.validation.nameTooLong', {}, 'Model name is limited to 100 characters')
- .then(text => showToast(text, 'warning'));
+ const text = safeTranslate('modelMetadata.validation.nameTooLong', {}, 'Model name is limited to 100 characters');
+ showToast(text, 'warning');
}
});
diff --git a/static/js/components/shared/ModelModal.js b/static/js/components/shared/ModelModal.js
index 6018fa1d..98a6a252 100644
--- a/static/js/components/shared/ModelModal.js
+++ b/static/js/components/shared/ModelModal.js
@@ -62,9 +62,9 @@ export async function showModelModal(model, modelType) {
}
// Generate tabs based on model type
- const examplesText = await safeTranslate('modals.model.tabs.examples', {}, 'Examples');
- const descriptionText = await safeTranslate('modals.model.tabs.description', {}, 'Model Description');
- const recipesText = await safeTranslate('modals.model.tabs.recipes', {}, 'Recipes');
+ const examplesText = safeTranslate('modals.model.tabs.examples', {}, 'Examples');
+ const descriptionText = safeTranslate('modals.model.tabs.description', {}, 'Model Description');
+ const recipesText = safeTranslate('modals.model.tabs.recipes', {}, 'Recipes');
const tabsContent = modelType === 'loras' ?
`
@@ -73,10 +73,10 @@ export async function showModelModal(model, modelType) {
`
`;
- const loadingExampleImagesText = await safeTranslate('modals.model.loading.exampleImages', {}, 'Loading example images...');
- const loadingDescriptionText = await safeTranslate('modals.model.loading.description', {}, 'Loading model description...');
- const loadingRecipesText = await safeTranslate('modals.model.loading.recipes', {}, 'Loading recipes...');
- const loadingExamplesText = await safeTranslate('modals.model.loading.examples', {}, 'Loading examples...');
+ const loadingExampleImagesText = safeTranslate('modals.model.loading.exampleImages', {}, 'Loading example images...');
+ const loadingDescriptionText = safeTranslate('modals.model.loading.description', {}, 'Loading model description...');
+ const loadingRecipesText = safeTranslate('modals.model.loading.recipes', {}, 'Loading recipes...');
+ const loadingExamplesText = safeTranslate('modals.model.loading.examples', {}, 'Loading examples...');
const tabPanesContent = modelType === 'loras' ?
`
diff --git a/static/js/components/shared/ModelTags.js b/static/js/components/shared/ModelTags.js
index 499b5889..3db60515 100644
--- a/static/js/components/shared/ModelTags.js
+++ b/static/js/components/shared/ModelTags.js
@@ -217,10 +217,10 @@ async function saveTags() {
// Exit edit mode
editBtn.click();
- showToast(await safeTranslate('modelTags.messages.updated', {}, 'Tags updated successfully'), 'success');
+ showToast(safeTranslate('modelTags.messages.updated', {}, 'Tags updated successfully'), 'success');
} catch (error) {
console.error('Error saving tags:', error);
- showToast(await safeTranslate('modelTags.messages.updateFailed', {}, 'Failed to update tags'), 'error');
+ showToast(safeTranslate('modelTags.messages.updateFailed', {}, 'Failed to update tags'), 'error');
}
}
@@ -362,24 +362,24 @@ function addNewTag(tag) {
// Validation: Check length
if (tag.length > 30) {
- safeTranslate('modelTags.validation.maxLength', {}, 'Tag should not exceed 30 characters')
- .then(text => showToast(text, 'error'));
+ const text = safeTranslate('modelTags.validation.maxLength', {}, 'Tag should not exceed 30 characters');
+ showToast(text, 'error');
return;
}
// Validation: Check total number
const currentTags = tagsContainer.querySelectorAll('.metadata-item');
if (currentTags.length >= 30) {
- safeTranslate('modelTags.validation.maxCount', {}, 'Maximum 30 tags allowed')
- .then(text => showToast(text, 'error'));
+ const text = safeTranslate('modelTags.validation.maxCount', {}, 'Maximum 30 tags allowed');
+ showToast(text, 'error');
return;
}
// Validation: Check for duplicates
const existingTags = Array.from(currentTags).map(tag => tag.dataset.tag);
if (existingTags.includes(tag)) {
- safeTranslate('modelTags.validation.duplicate', {}, 'This tag already exists')
- .then(text => showToast(text, 'error'));
+ const text = safeTranslate('modelTags.validation.duplicate', {}, 'This tag already exists');
+ showToast(text, 'error');
return;
}
diff --git a/static/js/core.js b/static/js/core.js
index adc5868c..6f659a71 100644
--- a/static/js/core.js
+++ b/static/js/core.js
@@ -29,6 +29,8 @@ export class AppCore {
// Initialize i18n first
window.i18n = i18n;
+ // Wait for i18n to be ready
+ await window.i18n.waitForReady();
console.log(`AppCore: Language set: ${i18n.getCurrentLocale()}`);
diff --git a/static/js/utils/i18nHelpers.js b/static/js/utils/i18nHelpers.js
index 141ef91f..0f717a91 100644
--- a/static/js/utils/i18nHelpers.js
+++ b/static/js/utils/i18nHelpers.js
@@ -23,21 +23,18 @@ export function translate(key, params = {}, fallback = null) {
}
/**
- * Safe translation function that waits for i18n to be ready
+ * Safe translation function. Assumes i18n is already ready.
* @param {string} key - Translation key
* @param {Object} params - Parameters for interpolation
* @param {string} fallback - Fallback text if translation fails
- * @returns {Promise} Translated text
+ * @returns {string} Translated text
*/
-export async function safeTranslate(key, params = {}, fallback = null) {
+export function safeTranslate(key, params = {}, fallback = null) {
if (!window.i18n) {
console.warn('i18n not available');
return fallback || key;
}
- // Wait for i18n to be ready
- await window.i18n.waitForReady();
-
const translation = window.i18n.t(key, params);
// If translation returned the key (meaning not found), use fallback
@@ -55,11 +52,11 @@ export async function safeTranslate(key, params = {}, fallback = null) {
* @param {Object} params - Parameters for interpolation
* @param {string} fallback - Fallback text
*/
-export async function updateElementText(element, key, params = {}, fallback = null) {
+export function updateElementText(element, key, params = {}, fallback = null) {
const el = typeof element === 'string' ? document.querySelector(element) : element;
if (!el) return;
- const text = await safeTranslate(key, params, fallback);
+ const text = safeTranslate(key, params, fallback);
el.textContent = text;
}
@@ -71,11 +68,11 @@ export async function updateElementText(element, key, params = {}, fallback = nu
* @param {Object} params - Parameters for interpolation
* @param {string} fallback - Fallback text
*/
-export async function updateElementAttribute(element, attribute, key, params = {}, fallback = null) {
+export function updateElementAttribute(element, attribute, key, params = {}, fallback = null) {
const el = typeof element === 'string' ? document.querySelector(element) : element;
if (!el) return;
- const text = await safeTranslate(key, params, fallback);
+ const text = safeTranslate(key, params, fallback);
el.setAttribute(attribute, text);
}
@@ -88,10 +85,9 @@ export async function updateElementAttribute(element, attribute, key, params = {
export function createReactiveTranslation(key, params = {}, callback) {
let currentLanguage = null;
- const updateTranslation = async () => {
+ const updateTranslation = () => {
if (!window.i18n) return;
- await window.i18n.waitForReady();
const newLanguage = window.i18n.getCurrentLocale();
// Only update if language changed or first time
@@ -121,18 +117,16 @@ export function createReactiveTranslation(key, params = {}, callback) {
* @param {Array} updates - Array of update configurations
* Each update should have: { element, key, type: 'text'|'attribute', attribute?, params?, fallback? }
*/
-export async function batchUpdateTranslations(updates) {
+export function batchUpdateTranslations(updates) {
if (!window.i18n) return;
- await window.i18n.waitForReady();
-
for (const update of updates) {
const { element, key, type = 'text', attribute, params = {}, fallback } = update;
if (type === 'text') {
- await updateElementText(element, key, params, fallback);
+ updateElementText(element, key, params, fallback);
} else if (type === 'attribute' && attribute) {
- await updateElementAttribute(element, attribute, key, params, fallback);
+ updateElementAttribute(element, attribute, key, params, fallback);
}
}
}
\ No newline at end of file