Skip to content

Instantly share code, notes, and snippets.

@knowlet
Last active September 30, 2025 01:56
Show Gist options
  • Save knowlet/8328f74465f701e271a5538d61347743 to your computer and use it in GitHub Desktop.
Save knowlet/8328f74465f701e271a5538d61347743 to your computer and use it in GitHub Desktop.
RPGMaker 動態翻譯系統 - 支援 mtool 工具的 key-value 翻譯檔案格式
//=============================================================================
// DynamicTranslation.js
//=============================================================================
/*:
* @plugindesc 動態翻譯系統 - 支援 mtool 工具的 key-value 翻譯檔案格式
* @author Supernova
*
* @param Default Language
* @desc 預設語言代碼 (例如: zh, en, ja)
* @default zh
*
* @param Translation Path
* @desc 翻譯檔案路徑 (相對於遊戲根目錄)
* @default translations/
*
* @param Auto Detect Translations
* @desc 是否自動偵測並載入所有可用的翻譯檔案
* @type boolean
* @default true
*
* @help
* ============================================================================
* 動態翻譯系統 - 支援 mtool 工具格式
* ============================================================================
*
* 此插件支援從 mtool 工具生成的 key-value 格式翻譯檔案。
* 可以在遊戲運行時動態載入翻譯檔案並即時切換語言。
*
* 翻譯檔案結構:
* translations/
* ├── zh.json (中文翻譯)
* ├── en.json (英文翻譯)
* └── ja.json (日文翻譯)
*
* 翻譯檔案格式 (mtool 工具生成的格式):
* {
* "原文文字1": "譯文文字1",
* "原文文字2": "譯文文字2",
* "等級": "Level",
* "HP": "HP",
* "MP": "MP",
* "攻擊": "Attack",
* "防禦": "Defense",
* "物品": "Item",
* "技能": "Skill",
* "裝備": "Equip",
* "儲存": "Save",
* "載入": "Load",
* "選項": "Options",
* "結束遊戲": "Exit Game",
* "新遊戲": "New Game",
* "繼續": "Continue",
* "取消": "Cancel",
* "買入": "Buy",
* "賣出": "Sell",
* "要儲存這個檔案嗎?": "Save this file?",
* "要載入這個檔案嗎?": "Load this file?",
* "%1 出現了!": "%1 appeared!",
* "%1 先發制人!": "%1 got the preemptive strike!",
* "%1 受到了 %2 點傷害!": "%1 took %2 damage!",
* "得到了 %1 %2!": "Gained %1 %2!",
* "得到了 %1 枚金幣!": "Gained %1 gold!"
* }
*
* 使用方法:
* 1. 使用 mtool 工具生成翻譯檔案並放在 translations/ 目錄中
* 2. 系統會自動載入所有可用的翻譯檔案
* 3. 在選項選單中選擇語言,或使用腳本呼叫切換語言
* 4. 所有介面文字會自動更新為新語言
*
* 腳本呼叫:
* TranslationManager.setLanguage('en'); // 切換到英文
* TranslationManager.getCurrentLanguage(); // 取得當前語言
* TranslationManager.getAvailableLanguages(); // 取得可用語言列表
*
* 插件命令:
* SetLanguage en // 切換到英文
* SetLanguage zh // 切換到中文
*
* ============================================================================
*/
(function() {
'use strict';
// 插件參數
var parameters = PluginManager.parameters('DynamicTranslation');
var defaultLanguage = parameters['Default Language'] || 'zh';
var translationPath = parameters['Translation Path'] || 'translations/';
var autoDetectTranslations = parameters['Auto Detect Translations'] === 'true';
// TranslationManager 類別 - 支援 mtool 工具的 key-value 格式
var TranslationManager = function() {
this._currentLanguage = defaultLanguage;
this._translations = {}; // key-value 格式的翻譯字典
this._originalTexts = {}; // 記錄原文的映射
this._isInitialized = false;
this._refreshCallbacks = [];
this._availableLanguages = [];
};
// 初始化翻譯管理器
TranslationManager.prototype.initialize = function() {
if (this._isInitialized) return;
this._buildOriginalTextMapping();
if (autoDetectTranslations) {
this._detectAvailableLanguages();
} else {
this._availableLanguages = [defaultLanguage];
this.loadLanguage(defaultLanguage, function() {
this._isInitialized = true;
this._applyTranslations();
}.bind(this));
}
};
// 建立原文對映(從 TextManager 和系統資料建立)
TranslationManager.prototype._buildOriginalTextMapping = function() {
this._originalTexts = {};
// 從 $dataSystem.terms 建立原文對映
if ($dataSystem && $dataSystem.terms) {
var terms = $dataSystem.terms;
for (var category in terms) {
if (terms.hasOwnProperty(category)) {
for (var id in terms[category]) {
if (terms[category].hasOwnProperty(id)) {
var originalText = terms[category][id];
if (originalText) {
this._originalTexts[originalText] = { category: category, id: id };
}
}
}
}
}
}
// 記錄貨幣單位
if ($dataSystem && $dataSystem.currencyUnit) {
this._originalTexts[$dataSystem.currencyUnit] = { category: 'currencyUnit', id: 'currencyUnit' };
}
};
// 自動偵測可用的語言檔案
TranslationManager.prototype._detectAvailableLanguages = function() {
var testFiles = ['zh', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'pt', 'ru'];
var loadedCount = 0;
testFiles.forEach(function(lang) {
this.loadLanguage(lang, function(success) {
loadedCount++;
if (success && this._availableLanguages.indexOf(lang) === -1) {
this._availableLanguages.push(lang);
}
// 當所有測試完成後,載入預設語言
if (loadedCount === testFiles.length) {
if (this._availableLanguages.length === 0) {
this._availableLanguages = [defaultLanguage];
}
this._isInitialized = true;
this._applyTranslations();
}
}.bind(this));
}.bind(this));
};
// 載入指定語言的翻譯檔案
TranslationManager.prototype.loadLanguage = function(language, callback) {
var filename = translationPath + language + '.json';
var xhr = new XMLHttpRequest();
xhr.open('GET', filename);
xhr.overrideMimeType('application/json');
xhr.onload = function() {
if (xhr.status < 400) {
try {
var translations = JSON.parse(xhr.responseText);
this._translations[language] = translations;
console.log('翻譯載入成功:', language, Object.keys(translations).length, '個項目');
console.log('載入的翻譯項目範例:', Object.keys(translations).slice(0, 5));
if (callback) callback(true);
} catch (e) {
console.error('翻譯檔案解析失敗:', filename, e);
if (callback) callback(false);
}
} else {
console.warn('翻譯檔案載入失敗:', filename, '狀態碼:', xhr.status);
if (callback) callback(false);
}
}.bind(this);
xhr.onerror = function() {
console.warn('無法載入翻譯檔案:', filename);
if (callback) callback(false);
};
xhr.send();
};
// 設定當前語言
TranslationManager.prototype.setLanguage = function(language) {
if (this._currentLanguage === language) return;
if (!this._translations[language]) {
this.loadLanguage(language, function(success) {
if (success) {
this._currentLanguage = language;
this._applyTranslations();
this._refreshAllWindows();
}
}.bind(this));
} else {
this._currentLanguage = language;
this._applyTranslations();
this._refreshAllWindows();
}
};
// 取得當前語言
TranslationManager.prototype.getCurrentLanguage = function() {
return this._currentLanguage;
};
// 取得可用語言列表
TranslationManager.prototype.getAvailableLanguages = function() {
return this._availableLanguages.slice();
};
// 翻譯文字(支援 mtool 工具的 key-value 格式)
TranslationManager.prototype.translate = function(originalText) {
if (!originalText || !this._isInitialized) {
return originalText;
}
var currentTranslations = this._translations[this._currentLanguage];
if (!currentTranslations) {
return originalText;
}
// 直接查找翻譯
var translatedText = currentTranslations[originalText];
if (translatedText !== undefined) {
// 調試:記錄成功翻譯的文字
if (Math.random() < 0.01) { // 只記錄 1% 的翻譯以避免刷屏
console.log('翻譯:', originalText.substring(0, 50) + (originalText.length > 50 ? '...' : ''), '->', translatedText.substring(0, 50) + (translatedText.length > 50 ? '...' : ''));
}
return translatedText;
}
// 如果找不到完整翻譯,嘗試清理可能的格式差異後再查找
var cleanedText = originalText.trim();
if (cleanedText !== originalText) {
translatedText = currentTranslations[cleanedText];
if (translatedText !== undefined) {
return translatedText;
}
}
return originalText;
};
// 取得翻譯系統狀態(用於調試)
TranslationManager.prototype.getStatus = function() {
return {
isInitialized: this._isInitialized,
currentLanguage: this._currentLanguage,
availableLanguages: this._availableLanguages,
loadedTranslations: Object.keys(this._translations),
translationCount: this._availableLanguages.reduce((count, lang) => {
return count + (this._translations[lang] ? Object.keys(this._translations[lang]).length : 0);
}, 0)
};
};
// 套用翻譯到 TextManager
TranslationManager.prototype._applyTranslations = function() {
if (!this._isInitialized) return;
// 備份原始的 TextManager 方法
if (!TextManager._originalBasic) {
TextManager._originalBasic = TextManager.basic;
TextManager._originalParam = TextManager.param;
TextManager._originalCommand = TextManager.command;
TextManager._originalMessage = TextManager.message;
TextManager._originalGetter = TextManager.getter;
}
var self = this;
// 覆蓋 TextManager 方法
TextManager.basic = function(basicId) {
var originalText = TextManager._originalBasic ? TextManager._originalBasic(basicId) : $dataSystem.terms.basic[basicId] || '';
return self.translate(originalText);
};
TextManager.param = function(paramId) {
var originalText = TextManager._originalParam ? TextManager._originalParam(paramId) : $dataSystem.terms.params[paramId] || '';
return self.translate(originalText);
};
TextManager.command = function(commandId) {
var originalText = TextManager._originalCommand ? TextManager._originalCommand(commandId) : $dataSystem.terms.commands[commandId] || '';
return self.translate(originalText);
};
TextManager.message = function(messageId) {
var originalText = TextManager._originalMessage ? TextManager._originalMessage(messageId) : $dataSystem.terms.messages[messageId] || '';
return self.translate(originalText);
};
// 處理動態屬性
if (TextManager._originalGetter) {
var originalGetter = TextManager._originalGetter;
TextManager.getter = function(method, param) {
return {
get: function() {
var originalText = this[method](param);
return self.translate(originalText);
}.bind(originalGetter(method, param))
};
};
}
// 處理貨幣單位
if (typeof TextManager.currencyUnit === 'object' && TextManager.currencyUnit.get) {
var originalCurrencyUnit = $dataSystem ? $dataSystem.currencyUnit : '';
Object.defineProperty(TextManager, 'currencyUnit', {
get: function() {
return self.translate(originalCurrencyUnit);
},
configurable: true
});
}
};
// 重新整理所有視窗
TranslationManager.prototype._refreshAllWindows = function() {
if (SceneManager._scene) {
SceneManager._scene._refreshAllWindows();
}
// 呼叫所有註冊的重新整理回呼
this._refreshCallbacks.forEach(function(callback) {
if (typeof callback === 'function') {
callback();
}
});
};
// 註冊重新整理回呼
TranslationManager.prototype.onRefresh = function(callback) {
if (typeof callback === 'function') {
this._refreshCallbacks.push(callback);
}
};
// 移除重新整理回呼
TranslationManager.prototype.offRefresh = function(callback) {
var index = this._refreshCallbacks.indexOf(callback);
if (index >= 0) {
this._refreshCallbacks.splice(index, 1);
}
};
// 建立全域實例
window.TranslationManager = TranslationManager;
window.$translationManager = new TranslationManager();
// 在 DataManager 載入完成後初始化翻譯管理器
var _DataManager_onLoad = DataManager.onLoad;
DataManager.onLoad = function(object) {
_DataManager_onLoad.call(this, object);
if (object === $dataSystem) {
// 系統資料載入完成後初始化翻譯管理器
$translationManager.initialize();
}
};
// 擴展 Scene_Base 來支援視窗重新整理
var _Scene_Base_create = Scene_Base.prototype.create;
Scene_Base.prototype.create = function() {
_Scene_Base_create.call(this);
this._refreshAllWindows = this._refreshAllWindows || function() {
this.children.forEach(function(child) {
if (child.refresh && typeof child.refresh === 'function') {
child.refresh();
}
});
};
};
// 擴展 Window_Options 來支援語言選擇
var _Window_Options_makeCommandList = Window_Options.prototype.makeCommandList;
Window_Options.prototype.makeCommandList = function() {
_Window_Options_makeCommandList.call(this);
this.addLanguageOption();
};
Window_Options.prototype.addLanguageOption = function() {
var languages = this.getAvailableLanguages();
if (languages.length > 1) {
this.addCommand('Language / 語言', 'language');
}
};
Window_Options.prototype.getAvailableLanguages = function() {
return $translationManager ? $translationManager.getAvailableLanguages() : ['zh'];
};
var _Window_Options_statusText = Window_Options.prototype.statusText;
Window_Options.prototype.statusText = function(index) {
var symbol = this.commandSymbol(index);
if (symbol === 'language') {
return this.getCurrentLanguageName();
}
return _Window_Options_statusText.call(this, index);
};
Window_Options.prototype.getCurrentLanguageName = function() {
var currentLang = $translationManager ? $translationManager.getCurrentLanguage() : 'zh';
var langNames = {
'zh': '中文',
'en': 'English',
'ja': '日本語',
'ko': '한국어',
'fr': 'Français',
'de': 'Deutsch',
'es': 'Español',
'pt': 'Português',
'ru': 'Русский'
};
return langNames[currentLang] || currentLang.toUpperCase();
};
Window_Options.prototype.processOk = function() {
var index = this.index();
var symbol = this.commandSymbol(index);
if (symbol === 'language') {
this.changeLanguage();
} else {
Window_Command.prototype.processOk.call(this);
}
};
Window_Options.prototype.changeLanguage = function() {
var languages = this.getAvailableLanguages();
var currentLang = $translationManager ? $translationManager.getCurrentLanguage() : 'zh';
var currentIndex = languages.indexOf(currentLang);
var nextIndex = (currentIndex + 1) % languages.length;
if ($translationManager) {
$translationManager.setLanguage(languages[nextIndex]);
ConfigManager.language = languages[nextIndex];
this.redrawCurrentItem();
SoundManager.playCursor();
}
};
// 擴展 ConfigManager 來支援語言設定
var _ConfigManager_makeData = ConfigManager.makeData;
ConfigManager.makeData = function() {
var config = _ConfigManager_makeData.call(this);
config.language = this.language;
return config;
};
var _ConfigManager_applyData = ConfigManager.applyData;
ConfigManager.applyData = function(config) {
_ConfigManager_applyData.call(this, config);
this.language = config.language || 'zh';
if ($translationManager && this.language !== $translationManager.getCurrentLanguage()) {
$translationManager.setLanguage(this.language);
}
};
// 插件命令
var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
_Game_Interpreter_pluginCommand.call(this, command, args);
if (command === 'SetLanguage' && args.length > 0) {
if ($translationManager) {
$translationManager.setLanguage(args[0]);
}
}
};
// 建立全域實例並公開 API
window.TranslationManager = TranslationManager;
window.$translationManager = new TranslationManager();
// 覆蓋 Game_Message 的 add 方法來支援翻譯
var _Game_Message_add = Game_Message.prototype.add;
Game_Message.prototype.add = function(text) {
if (window.$translationManager && window.$translationManager._isInitialized) {
text = window.$translationManager.translate(text);
}
_Game_Message_add.call(this, text);
};
// 覆蓋 Window_Message 的 startMessage 方法來支援多行翻譯
var _Window_Message_startMessage = Window_Message.prototype.startMessage;
Window_Message.prototype.startMessage = function() {
_Window_Message_startMessage.call(this);
// 如果翻譯系統已初始化,處理多行文字翻譯
if (window.$translationManager && window.$translationManager._isInitialized) {
var originalText = this._textState.text;
// 首先嘗試翻譯完整的多行文字
var fullTranslation = window.$translationManager.translate(originalText);
// 如果完整翻譯成功,檢查是否包含換行符並正確處理
if (fullTranslation !== originalText) {
this._textState.text = fullTranslation;
} else {
// 如果完整翻譯失敗,嘗試按行翻譯(保持向後兼容)
var translatedLines = [];
var lines = originalText.split('\n');
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (line.trim()) { // 只翻譯非空行
var translatedLine = window.$translationManager.translate(line);
translatedLines.push(translatedLine !== line ? translatedLine : line);
} else {
translatedLines.push(line); // 保留空行
}
}
// 重新組合翻譯後的文字
this._textState.text = translatedLines.join('\n');
}
}
};
// 為構造函數添加靜態方法
TranslationManager.getStatus = function() {
if (window.$translationManager) {
return window.$translationManager.getStatus();
}
return null;
};
TranslationManager.setLanguage = function(language) {
if (window.$translationManager) {
return window.$translationManager.setLanguage(language);
}
return null;
};
TranslationManager.getCurrentLanguage = function() {
if (window.$translationManager) {
return window.$translationManager.getCurrentLanguage();
}
return null;
};
TranslationManager.getAvailableLanguages = function() {
if (window.$translationManager) {
return window.$translationManager.getAvailableLanguages();
}
return [];
};
// 為 DTextPicture 插件提供的方法
TranslationManager.translateIfNeed = function(text, callback) {
if (window.$translationManager && window.$translationManager._isInitialized) {
var translatedText = window.$translationManager.translate(text);
if (callback && typeof callback === 'function') {
callback(translatedText);
}
return translatedText;
} else {
if (callback && typeof callback === 'function') {
callback(text);
}
return text;
}
};
// 立即初始化翻譯管理器(如果系統資料已載入)
var initTranslationManager = function() {
if (window.$translationManager && !$translationManager._isInitialized) {
if ($dataSystem) {
$translationManager.initialize();
} else {
// 在 DataManager 載入完成後初始化翻譯管理器
var _DataManager_onLoad = DataManager.onLoad;
DataManager.onLoad = function(object) {
_DataManager_onLoad.call(this, object);
if (object === $dataSystem) {
// 系統資料載入完成後初始化翻譯管理器
$translationManager.initialize();
}
};
}
}
};
// 嘗試立即初始化
initTranslationManager();
})();
//=============================================================================
// 翻譯系統測試腳本
//=============================================================================
/*:
* @plugindesc 測試翻譯系統功能
* @help
* 此腳本用於測試動態翻譯系統的功能。
*
* 使用方法:
* 1. 將此腳本加入遊戲項目中
* 2. 在遊戲中按 F12 開啟控制台
* 3. 執行以下測試指令:
*
* TranslationManager.getStatus(); // 取得系統狀態
* TranslationManager.setLanguage('zh'); // 切換到中文
* TranslationManager.getCurrentLanguage(); // 取得當前語言
* TranslationManager.getAvailableLanguages(); // 取得可用語言列表
*
* 或者使用插件命令:
* SetLanguage zh
*
* 注意:TranslationManager 是構造函數,$translationManager 是實例
*
* ============================================================================
*/
(function() {
'use strict';
// 檢查翻譯系統狀態
var checkTranslationSystem = function() {
console.log('=== 翻譯系統狀態檢查 ===');
if (typeof TranslationManager === 'undefined') {
console.error('❌ TranslationManager 未定義 - 插件可能未正確載入');
console.log('請確認 DynamicTranslation.js 插件已正確加入並啟用');
return false;
}
if (typeof $translationManager === 'undefined') {
console.error('❌ $translationManager 未定義 - 插件實例未正確創建');
return false;
}
if (typeof TranslationManager.getStatus !== 'function') {
console.error('❌ TranslationManager.getStatus 未定義 - 靜態方法未正確添加');
return false;
}
var status = TranslationManager.getStatus();
console.log('✅ 翻譯系統已載入');
console.log('📊 系統狀態:', status);
if (!status.isInitialized) {
console.warn('⚠️ 翻譯系統尚未初始化,可能還在載入中...');
}
return true;
};
// 測試翻譯功能
var testTranslations = function() {
console.log('=== 翻譯功能測試 ===');
if (!$translationManager._isInitialized) {
console.log('翻譯系統尚未初始化,跳過測試');
return;
}
// 測試基本文字翻譯
var testTexts = [
'等級', 'HP', 'MP', '攻擊', '防禦', '物品', '技能', '裝備', '儲存'
];
console.log('基本文字翻譯測試:');
testTexts.forEach(function(text) {
var translated = $translationManager ? $translationManager.translate(text) : text;
var status = translated !== text ? '✅' : '⚠️';
console.log(status + ' ' + text + ' -> ' + translated);
});
// 測試訊息翻譯
var messageTexts = [
'要儲存這個檔案嗎?',
'要載入這個檔案嗎?'
];
console.log('訊息翻譯測試:');
messageTexts.forEach(function(text) {
var translated = $translationManager.translate(text);
var status = translated !== text ? '✅' : '⚠️';
console.log(status + ' ' + text + ' -> ' + translated);
});
console.log('=== 翻譯測試完成 ===');
};
// 切換語言測試
var testLanguageSwitch = function() {
console.log('=== 語言切換測試 ===');
var languages = TranslationManager.getAvailableLanguages();
console.log('可用語言:', languages);
if (languages.length <= 1) {
console.log('只有一種語言可用,無法測試切換');
return;
}
var currentLang = TranslationManager.getCurrentLanguage();
var nextIndex = (languages.indexOf(currentLang) + 1) % languages.length;
var nextLang = languages[nextIndex];
console.log('當前語言:', currentLang, '切換到:', nextLang);
TranslationManager.setLanguage(nextLang);
console.log('切換完成');
};
// 公開測試函數到全域
window.testTranslationSystem = function() {
console.log('🚀 開始翻譯系統測試...');
var systemOk = checkTranslationSystem();
if (!systemOk) {
return '翻譯系統載入失敗,請檢查插件設定';
}
testTranslations();
return '測試完成,請查看控制台輸出';
};
// 測試 TextManager 翻譯(這是最重要的測試)
window.testTextManagerNow = function() {
console.log('🔍 開始 TextManager 翻譯測試...');
return testTextManager();
};
// 測試 Game_Message 翻譯
window.testGameMessageTranslation = function() {
console.log('=== Game_Message 翻譯測試 ===');
if (typeof $gameMessage === 'undefined') {
console.error('$gameMessage 未定義');
return;
}
try {
// 測試格式化文字
var testTexts = [
'測試訊息',
'得到 %1 經驗值!',
'戰鬥開始!'
];
console.log('測試 Game_Message 翻譯:');
testTexts.forEach(function(text, index) {
var translated = $translationManager ? $translationManager.translate(text) : text;
console.log('原始:', text, '翻譯:', translated);
});
// 測試實際的 $gameMessage.add
console.log('測試 $gameMessage.add 翻譯:');
$gameMessage.clear();
$gameMessage.add('測試訊息');
$gameMessage.add('得到 100 經驗值!');
var allText = $gameMessage.allText();
console.log('Game_Message 所有文字:', allText);
} catch (e) {
console.error('Game_Message 測試失敗:', e);
}
return 'Game_Message 翻譯測試完成';
};
// 檢查翻譯載入狀態
window.checkTranslationLoading = function() {
console.log('=== 翻譯載入狀態檢查 ===');
console.log('翻譯系統初始化狀態:', TranslationManager.getStatus());
if ($translationManager && $translationManager._translations) {
console.log('載入的翻譯語言:', Object.keys($translationManager._translations));
if ($translationManager._translations['zh']) {
console.log('中文翻譯項目數量:', Object.keys($translationManager._translations['zh']).length);
console.log('中文翻譯範例:', Object.keys($translationManager._translations['zh']).slice(0, 10));
}
}
return '翻譯載入檢查完成';
};
// 強制刷新選項選單
window.forceRefreshOptions = function() {
console.log('🔄 強制刷新選項選單...');
if (SceneManager._scene && SceneManager._scene._optionsWindow) {
SceneManager._scene._optionsWindow.refresh();
console.log('選項選單已刷新');
} else {
console.log('無法找到選項選單');
}
return '刷新完成';
};
// 檢查 TextManager 翻譯
window.testTextManager = function() {
console.log('=== TextManager 翻譯測試 ===');
if (typeof TextManager === 'undefined') {
console.error('TextManager 未定義');
return;
}
try {
// 檢查原始 $dataSystem.terms 的值
console.log('原始 $dataSystem.terms 值:');
if ($dataSystem && $dataSystem.terms) {
console.log('basic[0]:', $dataSystem.terms.basic[0], '(索引 0)');
console.log('basic[2]:', $dataSystem.terms.basic[2], '(索引 2)');
console.log('commands[0]:', $dataSystem.terms.commands[0], '(指令索引 0)');
console.log('messages[saveMessage]:', $dataSystem.terms.messages['saveMessage'], '(訊息 saveMessage)');
// 檢查更多項目來確認
console.log('commands[1]:', $dataSystem.terms.commands[1], '(指令索引 1)');
console.log('commands[2]:', $dataSystem.terms.commands[2], '(指令索引 2)');
}
var testTexts = [
TextManager.basic(0), // 基本屬性 0
TextManager.basic(2), // 基本屬性 2
TextManager.command(0), // 指令 0 (戰鬥)
TextManager.message('saveMessage') // 訊息 saveMessage
];
console.log('測試項目說明:');
console.log(' basic(0): 基本屬性索引 0');
console.log(' basic(2): 基本屬性索引 2');
console.log(' command(0): 指令索引 0 (應該是 戦う/戰鬥)');
console.log(' message(saveMessage): 訊息 saveMessage (應該是 save)');
// 測試格式化文字
console.log('測試格式化文字翻譯:');
var formatTest = '得到 100 經驗值!';
var translatedFormat = $translationManager ? $translationManager.translate(formatTest) : formatTest;
console.log('格式化測試:', formatTest, '->', translatedFormat);
console.log('當前 TextManager 輸出:');
testTexts.forEach(function(text, index) {
var expectedTranslations = [
'等級', // basic(0) 應該翻譯為 "等級"
'HP', // basic(2) 應該保持 "HP"
'戰鬥', // command(0) 應該翻譯為 "戰鬥"
'要儲存這個檔案嗎?' // message(saveMessage) 應該翻譯為 "要儲存這個檔案嗎?"
];
var status = text === expectedTranslations[index] ? '✅' : '❌';
console.log('TextManager[' + index + ']:', text, status, '(預期:', expectedTranslations[index] + ')');
});
// 檢查翻譯系統狀態
console.log('翻譯系統狀態:', TranslationManager.getStatus());
// 檢查 TextManager 原始方法是否存在
console.log('TextManager 原始方法檢查:');
console.log('_originalBasic:', !!TextManager._originalBasic);
console.log('_originalParam:', !!TextManager._originalParam);
console.log('_originalCommand:', !!TextManager._originalCommand);
console.log('_originalMessage:', !!TextManager._originalMessage);
} catch (e) {
console.error('TextManager 測試失敗:', e);
}
return 'TextManager 測試完成';
};
window.testLanguageSwitch = function() {
console.log('🔄 開始語言切換測試...');
testLanguageSwitch();
return '語言切換測試完成';
};
// 自動檢查系統狀態
console.log('翻譯系統載入檢查中...');
setTimeout(function() {
checkTranslationSystem();
}, 500);
console.log('💡 提示: 使用 testTranslationSystem() 進行完整測試');
console.log('💡 提示: 使用 testLanguageSwitch() 測試語言切換');
// 自動載入測試腳本(如果尚未載入)
if (typeof window.testTranslationSystem === 'undefined') {
console.log('🔧 測試腳本載入中...');
// 測試腳本已經被包含了,所以不需要動態載入
}
// 提供載入指示
console.log('💡 測試指令:');
console.log(' testTextManager() - 測試 TextManager 翻譯');
console.log(' testTranslationSystem() - 完整翻譯系統測試');
console.log(' forceRefreshOptions() - 強制刷新選項選單');
console.log(' TranslationManager.getStatus() - 檢查系統狀態');
})();

動態翻譯系統使用指南

概述

此翻譯系統支援動態載入 mtool 工具生成的 key-value 格式翻譯檔案,可以在遊戲運行時即時切換語言並更新所有介面文字。

檔案結構

www/
├── js/
│   └── plugins/
│       └── DynamicTranslation.js    # 主要翻譯插件
├── translations/                    # 翻譯檔案目錄
│   ├── zh.json                     # 中文翻譯
│   ├── en.json                     # 英文翻譯
│   └── ja.json                     # 日文翻譯
└── test_translation_system.js      # 測試腳本

翻譯檔案格式

翻譯檔案使用 JSON 格式,支援 mtool 工具的 key-value 格式:

{
  "原文文字1": "譯文文字1",
  "原文文字2": "譯文文字2",
  "等級": "Level",
  "HP": "HP",
  "攻擊": "Attack",
  "要儲存這個檔案嗎?": "Save this file?",
  "%1 出現了!": "%1 appeared!"
}

使用方法

1. 安裝插件

DynamicTranslation.js 加入遊戲的插件列表中,並啟用它。

2. 準備翻譯檔案

使用 mtool 工具生成翻譯檔案,並將其放在 translations/ 目錄中。支援的語言代碼:

  • zh: 中文
  • en: 英文
  • ja: 日文
  • ko: 韓文
  • fr: 法文
  • de: 德文
  • es: 西班牙文
  • pt: 葡萄牙文
  • ru: 俄文

3. 在遊戲中使用

方法一:選項選單

系統會自動在選項選單中加入語言選擇項目。玩家可以:

  1. 開啟遊戲選單
  2. 選擇「選項」
  3. 找到「Language / 語言」選項
  4. 按確認鍵切換語言

方法二:腳本呼叫

在事件中使用腳本呼叫:

TranslationManager.setLanguage('en');     // 切換到英文
TranslationManager.setLanguage('ja');     // 切換到日文
TranslationManager.setLanguage('zh');     // 切換到中文

方法三:插件命令

在事件中使用插件命令:

SetLanguage en    // 切換到英文
SetLanguage ja    // 切換到日文
SetLanguage zh    // 切換到中文

API 參考

TranslationManager 物件

// 取得當前語言
TranslationManager.getCurrentLanguage()

// 取得可用語言列表
TranslationManager.getAvailableLanguages()

// 切換語言
TranslationManager.setLanguage(languageCode)

// 翻譯特定文字
TranslationManager.translate(originalText)

插件參數

在插件管理器中設定:

  • Default Language: 預設語言代碼 (預設: zh)
  • Translation Path: 翻譯檔案路徑 (預設: translations/)
  • Auto Detect Translations: 是否自動偵測翻譯檔案 (預設: true)

工作原理

  1. 初始化階段: 系統載入 $dataSystem.terms 建立原文對映表
  2. 載入階段: 根據設定載入對應語言的翻譯檔案
  3. 覆蓋階段: 覆蓋 TextManager 的方法,讓所有文字查詢都經過翻譯處理
  4. 切換階段: 當語言切換時,重新整理所有視窗以更新顯示文字

測試

包含 test_translation_system.js 測試腳本,可以用來驗證翻譯系統功能。

在瀏覽器控制台中執行:

testTranslationSystem();

支援的文字類型

系統支援翻譯以下類型的文字:

  • 基本屬性名稱 (等級、HP、MP等)
  • 指令文字 (戰鬥、物品、技能等)
  • 系統訊息 (儲存確認、載入確認等)
  • 戰鬥訊息 (傷害、恢復、獲得物品等)
  • 介面文字 (選項、按鈕等)

注意事項

  1. 翻譯檔案必須使用 UTF-8 編碼
  2. 系統會自動處理含參數的文字 (如 %1 出現了!)
  3. 未找到翻譯的文字會顯示原文
  4. 建議先建立完整的中英文翻譯,再擴展到其他語言
  5. 翻譯檔案載入失敗不會影響遊戲正常運行
{
"": "",
"レベル": "等級",
"HP": "HP",
"MP": "MP",
"TP": "TP",
"EXP": "EXP",
"最大HP": "最大HP",
"最大MP": "最大MP",
"攻撃力": "攻擊力",
"防御力": "防禦力",
"魔法攻撃": "魔法攻擊",
"魔法防御": "魔法防禦",
"敏捷性": "敏捷",
"運": "幸運",
"戦う": "戰鬥",
"逃げる": "逃跑",
"攻撃": "攻擊",
"防御": "防禦",
"アイテム": "物品",
"スキル": "技能",
"装備": "裝備",
"ステータス": "狀態",
"並び替え": "隊列",
"save": "要儲存這個檔案嗎?",
"exit": "結束遊戲",
"option": "選項",
"武器": "武器",
"防具": "防具",
"大事なもの": "重要物品",
"最強装備": "最佳化",
"全て外す": "清除",
"new game": "新遊戲",
"continue": "繼續",
"title": "返回標題",
"cancel": "取消",
"購入する": "買入",
"売却する": "賣出",
"常時ダッシュ": "常時衝刺",
"コマンド記憶": "記住指令",
"BGM volume": "BGM 音量",
"BGS volume": "BGS 音量",
"ME volume": "ME 音量",
"SE volume": "SE 音量",
"持っている数": "持有",
"現在の%1": "目前 EXP",
"次の%1まで": "下次等級",
"load": "要載入這個檔案嗎?",
"file": "檔案",
"%1たち": "隊伍名稱",
"円": "枚金幣",
"%1が出現!": "%1 出現了!",
"%1は先手を取った!": "%1 先發制人!",
"%1は不意をつかれた!": "%1 受到奇襲!",
"は逃げ出した!": "開始逃跑!",
"しかし逃げることはできなかった!": "但是逃跑失敗了!",
"の勝利!": "勝利了!",
"は戦いに敗れた。": "失敗了!",
"%1 の%2を獲得!": "得到了 %1 %2!",
"GOLD + %1\\!": "得到了 %1 枚金幣!",
"%1GET!": "得到了 %1!",
"LEVEL UP!": "升級了!",
"%1を覚えた!": "學會了 %1!",
"%2": "使用了 %2!",
"会心の一撃!!": "會心一擊!!",
"HP-%2": "受到了 %2 點傷害!",
"%2+ %3": "恢復了 %2 點%3!",
"%1の%2が %3 増えた!": "獲得了 %2 點%3!",
"%1の%2が %3 減った!": "失去了 %2 點%3!",
"%1は%2を %3 奪われた!": "被吸取了 %2 點%3!",
"%1はダメージを受けていない!": "沒有受到傷害!",
"MISS!": "閃避了攻擊!",
"%1にダメージを与えられない!": "沒有受到傷害!",
"ミス! %1にダメージを与えられない!": "閃避了攻擊!",
"%1は攻撃をかわした!": "閃避了攻擊!",
"%1は魔法を打ち消した!": "閃避了魔法!",
"%1は魔法を跳ね返した!": "反射了魔法!",
"%1の反撃!": "進行了反擊!",
"%1が%2をかばった!": "保護了 %2!",
"%1の%2が上がった!": "增加了!",
"%1の%2が下がった!": "減少了!",
"%1の%2が元に戻った!": "解除了!",
"%1には効かなかった!": "行動失敗了!"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment