diff --git a/py/routes/base_model_routes.py b/py/routes/base_model_routes.py
index 38c44152..3a832c9b 100644
--- a/py/routes/base_model_routes.py
+++ b/py/routes/base_model_routes.py
@@ -130,18 +130,10 @@ class BaseModelRoutes(ABC):
'is_initializing': is_initializing,
'settings': settings,
'request': request,
- 'user_language': user_language, # 传递语言设置到模板
'folders': [],
# 添加服务端翻译函数
't': server_i18n.get_translation,
'server_i18n': server_i18n,
- # 添加一些常用的翻译到上下文,避免在模板中频繁调用
- 'common_translations': {
- 'loading': server_i18n.get_translation('common.status.loading'),
- 'error': server_i18n.get_translation('common.status.error'),
- 'refresh': server_i18n.get_translation('common.actions.refresh'),
- 'search': server_i18n.get_translation('common.actions.search'),
- }
}
if not is_initializing:
diff --git a/py/routes/recipe_routes.py b/py/routes/recipe_routes.py
index f392c250..f23e0fd7 100644
--- a/py/routes/recipe_routes.py
+++ b/py/routes/recipe_routes.py
@@ -149,17 +149,9 @@ class RecipeRoutes:
is_initializing=False,
settings=settings,
request=request,
- user_language=user_language,
# 添加服务端翻译函数
t=server_i18n.get_translation,
server_i18n=server_i18n,
- # 添加一些常用的翻译到上下文
- common_translations={
- 'loading': server_i18n.get_translation('common.status.loading'),
- 'error': server_i18n.get_translation('common.status.error'),
- 'refresh': server_i18n.get_translation('common.actions.refresh'),
- 'search': server_i18n.get_translation('common.actions.search'),
- }
)
except Exception as cache_error:
logger.error(f"Error loading recipe cache data: {cache_error}")
@@ -169,15 +161,9 @@ class RecipeRoutes:
is_initializing=True,
settings=settings,
request=request,
- user_language=user_language,
# 添加服务端翻译函数
t=server_i18n.get_translation,
server_i18n=server_i18n,
- # 添加一些常用的翻译到上下文
- common_translations={
- 'loading': server_i18n.get_translation('common.status.loading'),
- 'error': server_i18n.get_translation('common.status.error'),
- }
)
logger.info("Recipe cache error, returning initialization page")
diff --git a/py/routes/stats_routes.py b/py/routes/stats_routes.py
index 4b1c76a6..36392e2e 100644
--- a/py/routes/stats_routes.py
+++ b/py/routes/stats_routes.py
@@ -75,16 +75,9 @@ class StatsRoutes:
is_initializing=is_initializing,
settings=settings,
request=request,
- user_language=user_language,
# 添加服务端翻译函数
t=server_i18n.get_translation,
server_i18n=server_i18n,
- # 添加一些常用的翻译到上下文
- common_translations={
- 'loading': server_i18n.get_translation('common.status.loading'),
- 'error': server_i18n.get_translation('common.status.error'),
- 'refresh': server_i18n.get_translation('common.actions.refresh'),
- }
)
return web.Response(
diff --git a/static/js/i18n/index.js b/static/js/i18n/index.js
index 5c3e697c..a480afac 100644
--- a/static/js/i18n/index.js
+++ b/static/js/i18n/index.js
@@ -33,20 +33,10 @@ class I18nManager {
}
/**
- * Get language from user settings with fallback to browser detection
+ * Get language from user settings with fallback to English
* @returns {string} Language code
*/
getLanguageFromSettings() {
- // 优先使用后端传递的初始语言
- if (window.__INITIAL_LANGUAGE__ && this.locales[window.__INITIAL_LANGUAGE__]) {
- return window.__INITIAL_LANGUAGE__;
- }
-
- // 检查服务端传递的翻译数据
- if (window.__SERVER_TRANSLATIONS__ && window.__SERVER_TRANSLATIONS__.language && this.locales[window.__SERVER_TRANSLATIONS__.language]) {
- return window.__SERVER_TRANSLATIONS__.language;
- }
-
// Check localStorage for user-selected language
const STORAGE_PREFIX = 'lora_manager_';
let userLanguage = null;
@@ -66,8 +56,8 @@ class I18nManager {
return userLanguage;
}
- // Fallback to browser language detection for first-time users
- return this.detectLanguage();
+ // Fallback to English
+ return 'en';
}
/**
@@ -123,29 +113,6 @@ class I18nManager {
];
}
- /**
- * Detect browser language with fallback to English (for first-time users)
- * @returns {string} Language code
- */
- detectLanguage() {
- // Get browser language
- const browserLang = navigator.language || navigator.languages[0] || 'en';
-
- // Check if we have exact match
- if (this.locales[browserLang]) {
- return browserLang;
- }
-
- // Check for language without region (e.g., 'zh' from 'zh-CN')
- const langCode = browserLang.split('-')[0];
- if (this.locales[langCode]) {
- return langCode;
- }
-
- // Fallback to English
- return 'en';
- }
-
/**
* Get translation for a key with optional parameters
* @param {string} key - Translation key (supports dot notation)
@@ -255,7 +222,7 @@ class I18nManager {
}
/**
- * Initialize i18n from user settings instead of browser detection
+ * Initialize i18n from user settings
* This prevents language flashing on page load
*/
async initializeFromSettings() {
diff --git a/static/js/utils/i18nHelpers.js b/static/js/utils/i18nHelpers.js
index 14a8a745..582775e5 100644
--- a/static/js/utils/i18nHelpers.js
+++ b/static/js/utils/i18nHelpers.js
@@ -9,14 +9,14 @@ import { i18n } from '../i18n/index.js';
*/
export function translateDOM() {
if (!window.i18n) return;
-
+
// Select all elements with data-i18n attributes, including optgroups and options
const elements = document.querySelectorAll('[data-i18n]');
-
+
elements.forEach(element => {
const key = element.getAttribute('data-i18n');
const target = element.getAttribute('data-i18n-target') || 'textContent';
-
+
if (key) {
const translation = window.i18n.t(key);
@@ -162,31 +162,23 @@ export function formatNumber(number, options = {}) {
* This should be called after DOM content is loaded
*/
export function initializePageI18n() {
- // 优先使用服务端传递的翻译数据,避免闪烁
- if (window.__SERVER_TRANSLATIONS__ && window.__SERVER_TRANSLATIONS__.language) {
- // 设置客户端i18n的语言为服务端传递的语言
- if (window.i18n && window.i18n.setLanguage) {
- window.i18n.setLanguage(window.__SERVER_TRANSLATIONS__.language);
- }
+ // Always use the client-side i18n with user settings
+ if (window.i18n) {
+ // Translate DOM elements
+ translateDOM();
- // 对于剩余的需要动态翻译的元素,仍使用客户端翻译
- translateDOM();
- } else {
- // 回退到完整的客户端翻译
- translateDOM();
- }
-
- // Update search placeholder based on current page
- const currentPath = window.location.pathname;
- updateSearchPlaceholder(currentPath);
-
- // Set document direction for RTL languages
- if (i18n.isRTL()) {
- document.documentElement.setAttribute('dir', 'rtl');
- document.body.classList.add('rtl');
- } else {
- document.documentElement.setAttribute('dir', 'ltr');
- document.body.classList.remove('rtl');
+ // Update search placeholder based on current page
+ const currentPath = window.location.pathname;
+ updateSearchPlaceholder(currentPath);
+
+ // Set document direction for RTL languages
+ if (i18n.isRTL()) {
+ document.documentElement.setAttribute('dir', 'rtl');
+ document.body.classList.add('rtl');
+ } else {
+ document.documentElement.setAttribute('dir', 'ltr');
+ document.body.classList.remove('rtl');
+ }
}
}
diff --git a/static/js/utils/mixedI18n.js b/static/js/utils/mixedI18n.js
deleted file mode 100644
index e38a0d26..00000000
--- a/static/js/utils/mixedI18n.js
+++ /dev/null
@@ -1,172 +0,0 @@
-/**
- * Mixed i18n handler - coordinates server-side and client-side translations
- * Reduces language flashing by using server-rendered content initially
- */
-
-class MixedI18nHandler {
- constructor() {
- this.serverTranslations = window.__SERVER_TRANSLATIONS__ || {};
- this.currentLanguage = this.serverTranslations.language || 'en';
- this.initialized = false;
- }
-
- /**
- * Initialize mixed i18n system
- */
- async initialize() {
- if (this.initialized) return;
-
- // Import the main i18n module
- const { i18n } = await import('/loras_static/js/i18n/index.js');
- this.clientI18n = i18n;
-
- // Ensure client i18n uses the same language as server
- if (this.currentLanguage && this.clientI18n.getCurrentLocale() !== this.currentLanguage) {
- this.clientI18n.setLanguage(this.currentLanguage);
- }
-
- // Translate any remaining elements that need client-side translation
- this.translateRemainingElements();
-
- this.initialized = true;
-
- // Dispatch event to notify that mixed i18n is ready
- window.dispatchEvent(new CustomEvent('mixedI18nReady', {
- detail: { language: this.currentLanguage }
- }));
- }
-
- /**
- * Translate elements that still need client-side translation
- * (primarily dynamic content and complex components)
- */
- translateRemainingElements() {
- if (!this.clientI18n) return;
-
- // Find all elements with data-i18n attribute that haven't been server-rendered
- const elements = document.querySelectorAll('[data-i18n]');
-
- elements.forEach(element => {
- // Skip if already translated by server (check if content matches key pattern)
- const key = element.getAttribute('data-i18n');
- const currentContent = element.textContent || element.value || element.placeholder;
-
- // If the current content looks like a translation key, translate it
- if (currentContent === key || currentContent.includes('.') || currentContent === '') {
- this.translateElement(element, key);
- }
- });
- }
-
- /**
- * Translate a single element using client-side i18n
- */
- translateElement(element, key) {
- if (!this.clientI18n) return;
-
- const params = element.getAttribute('data-i18n-params');
- let parsedParams = {};
-
- if (params) {
- try {
- parsedParams = JSON.parse(params);
- } catch (e) {
- console.warn(`Invalid JSON in data-i18n-params for key ${key}:`, params);
- }
- }
-
- // Get translated text
- const translatedText = this.clientI18n.t(key, parsedParams);
-
- // Handle different translation targets
- const target = element.getAttribute('data-i18n-target') || 'textContent';
-
- switch (target) {
- case 'placeholder':
- element.placeholder = translatedText;
- break;
- case 'title':
- element.title = translatedText;
- break;
- case 'alt':
- element.alt = translatedText;
- break;
- case 'innerHTML':
- element.innerHTML = translatedText;
- break;
- case 'textContent':
- default:
- element.textContent = translatedText;
- break;
- }
- }
-
- /**
- * Get current language
- */
- getCurrentLanguage() {
- return this.currentLanguage;
- }
-
- /**
- * Get translation using client-side i18n (for dynamic content)
- */
- t(key, params = {}) {
- if (this.clientI18n) {
- return this.clientI18n.t(key, params);
- }
-
- // Fallback: check server translations
- if (this.serverTranslations.common && key.startsWith('common.')) {
- const subKey = key.substring(7); // Remove 'common.' prefix
- return this.serverTranslations.common[subKey] || key;
- }
-
- return key;
- }
-
- /**
- * Format file size using client-side i18n
- */
- formatFileSize(bytes, decimals = 2) {
- if (this.clientI18n) {
- return this.clientI18n.formatFileSize(bytes, decimals);
- }
-
- // Simple fallback
- if (bytes === 0) return '0 Bytes';
- const k = 1024;
- const dm = decimals < 0 ? 0 : decimals;
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
- const i = Math.floor(Math.log(bytes) / Math.log(k));
- return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
- }
-
- /**
- * Format date using client-side i18n
- */
- formatDate(date, options = {}) {
- if (this.clientI18n) {
- return this.clientI18n.formatDate(date, options);
- }
-
- // Simple fallback
- const dateObj = date instanceof Date ? date : new Date(date);
- return dateObj.toLocaleDateString();
- }
-}
-
-// Create global instance
-window.mixedI18n = new MixedI18nHandler();
-
-// Auto-initialize when DOM is ready
-if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', () => {
- window.mixedI18n.initialize();
- });
-} else {
- window.mixedI18n.initialize();
-}
-
-// Export for module usage
-export default window.mixedI18n;
diff --git a/templates/base.html b/templates/base.html
index 2404b4b8..7464ea1b 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -50,23 +50,6 @@
{% else %}
-
-
{% block main_script %}{% endblock %}
{% endif %}