diff --git a/lib/ocelot-brain b/lib/ocelot-brain index 4b2d2fc..6aaded3 160000 --- a/lib/ocelot-brain +++ b/lib/ocelot-brain @@ -1 +1 @@ -Subproject commit 4b2d2fcec19f8e238dd1c4bdb09d42b11e9bb6e6 +Subproject commit 6aaded3ec6405c442fc7fec4381213494d20d162 diff --git a/src/main/resources/ocelot/desktop/colorscheme.txt b/src/main/resources/ocelot/desktop/colorscheme.txt index 1878c16..af4df72 100644 --- a/src/main/resources/ocelot/desktop/colorscheme.txt +++ b/src/main/resources/ocelot/desktop/colorscheme.txt @@ -24,6 +24,8 @@ Tier3 = #c354cd Label = #333333 LabelError = #aa0000 +LabelUpdateAvailable = #33aa33 + Scrollbar = #e5e5e526 ScrollbarThumb = #cc3f72 diff --git a/src/main/resources/ocelot/desktop/lang/en_US.lang b/src/main/resources/ocelot/desktop/lang/en_US.lang new file mode 100644 index 0000000..8da25f8 --- /dev/null +++ b/src/main/resources/ocelot/desktop/lang/en_US.lang @@ -0,0 +1,359 @@ +# This is the English master file for localizations. It will always be the most +# up-to-date version, so other localizations should be based on this one. +# Use [nl] to for a line break. + +# Buttons +button.ok = Ok +button.cancel = Cancel +button.yes = Yes +button.no = No +button.proceed = Proceed +button.download = Download +button.apply = Apply + +# Loading screen +label.loading.brain = Initializing brain... +label.loading.gui = Initializing GUI... +label.loading.resources = Loading resources... +label.loading.workspace = Loading workspace... + +# Titles +title.profiler = Profiler + +# Windows +label.window.computer = Computer +label.window.server = Server +label.window.microcontroller = Microcontroller +label.window.computer.toggleStats = Toggle computer usage histogram +label.window.computer.componentUsage = Component usage +label.window.computer.memoryUsage = Memory +label.window.computer.cpuUsage = CPU +label.window.computer.callBudget = Call budget + +label.window.disk.used = Used +label.window.disk.capacity = Capacity +label.window.disk.label = Label +label.window.disk.makeReadOnly = Make read-only +label.window.disk.make.managed = Make managed +label.window.disk.make.unmanaged = Make unmanaged + +label.window.ocelot.messages = Messages +label.window.ocelot.limit = Limit +label.window.ocelot.scrollToEnd = Scroll to end +label.window.ocelot.clear = Clear + +label.window.rack.enabled = Enabled +label.window.rack.disabled = Disabled + +label.window.raid.warning.addingDisk = Adding a disk wipes it. +label.window.raid.warning.removingDisk = Removing a disk wipes the raid. + +label.window.radio.screenColor = Screen color +label.window.radio.screenText = Screen text + +label.window.relay.cycleRate = Cycle rate +label.window.relay.packetsPerCycle = Packets / cycle +label.window.relay.queueSize = Queue size + +label.window.tapeDrive.unnamedTape = Unnamed tape +label.window.tapeDrive.noTape = No tape + +# Menu +label.menu.file = File +label.menu.file.new = New +label.menu.file.open = Open +label.menu.file.save = Save +label.menu.file.saveAs = Save as +label.menu.file.exit = Exit + +label.menu.player = Player +label.menu.player.new = Create new +label.menu.player.remove = Remove + +label.menu.settings = Settings + +label.menu.help = Help +label.menu.help.updates = Check for Updates +label.menu.help.manual = Manual +label.menu.help.about = About + +label.menu.copyAddress = Copy address +label.menu.disconnect = Disconnect +label.menu.remove = Remove +label.menu.setLabel = Set label +label.menu.turnOn = Turn on +label.menu.turnOff = Turn off +label.menu.reboot = Reboot +label.menu.ejectFloppy = Eject floppy +label.menu.changeFloppy = Change floppy +label.menu.setFloppy = Set floppy +label.menu.changeSimulationSpeed = Change simulation speed +label.menu.resetSimulationSpeed = Reset simulation speed +label.menu.setup = Set up +label.menu.instrument = Instrument +label.menu.setAspectRatio = Set aspect ratio +label.menu.removeKeyboard = Remove keyboard +label.menu.addKeyboard = Add keyboard +label.menu.changeTier = Change tier +label.menu.eject = Eject +label.menu.setArchitecture = Set architecture +label.menu.currentArchitecture = current +label.menu.externalDataSource = External data source +label.menu.localFile = Local file +label.menu.fileViaUrl = File via URL +label.menu.detach = Detach +label.menu.setDirectory = Set directory +label.menu.changeDirectory = Change directory +label.menu.resetDirectory = Reset directory +label.menu.editDisk = Edit disk +label.menu.setChannel = Set channel +label.menu.openConsole = Open console +label.menu.redstoneIO = Redstone I/O +label.menu.bundledIO = Bundled I/O +label.menu.openCardInterface = Open card interface + +# Status bar +label.bar.menu = Menu +label.bar.addNode = Add node +label.bar.moveNode = Move node +label.bar.connectDisconnect = Connect/Disconnect +label.bar.turnServerOnOff = Turn server on/off +label.bar.playSample = Play sample +label.bar.open = Open +label.bar.close = Close +label.bar.closeWindow = Close window +label.bar.rotateView = Rotate view +label.bar.panView = Pan view +label.bar.resetCamera = Reset camera +label.bar.showGrid = Show grid +label.bar.closeSelector = Close selector +label.bar.moveCamera = Move camera +label.bar.scaleScreen = Scale screen +label.bar.scaleScreenMagnify = Scale screen (magnify) + +# Settings +label.settings.ui = UI +label.settings.ui.language = Language: +label.settings.ui.pinWindows = Pin newly opened windows +label.settings.ui.fullscreen = Fullscreen mode +label.settings.ui.saveOnExit = Save workspace on exit +label.settings.ui.openLastWorkspace = Re-open last workspace on startup +label.settings.ui.screenPreview = Show previews on screen blocks +label.settings.ui.mipmaps = Enable mipmaps for screen windows +label.settings.ui.festiveDecorations = Enable festive decorations +label.settings.ui.scale = Interface scale +label.settings.ui.tooltipDelay = Tooltip delays: +label.settings.ui.tooltip.items = Items +label.settings.ui.tooltip.ui = UI Elements + +label.settings.sound = Sound +label.settings.sound.master = Master volume +label.settings.sound.music = Music blocks volume +label.settings.sound.environment = Environment volume +label.settings.sound.beep = Beep volume +label.settings.sound.ui = Interface volume +label.settings.sound.positional = Use positional sound + +label.settings.system = System +label.settings.system.confPath = Set OpenComputers configuration file path: +label.settings.system.confPathPlaceholder = not set / using default OpenComputers configuration +label.settings.system.confSearch = Search for OpenComputers configuration file +label.settings.system.confGenerate = Generate new OpenComputers configuration file in: +label.settings.system.currentDir = Current Directory +label.settings.system.configDir = Config Directory +label.settings.system.customDir = Custom Path +label.settings.system.autosave = Workspace autosave +label.settings.system.autosavePeriod = Workspace autosave period (seconds): +label.settings.system.restart = Restart Ocelot to apply new configuration + +# Dialogs +label.dialog.addNewPlayer = Add new player + +label.dialog.about.subtitle = OpenComputers Emulator +label.dialog.about.version = Version +label.dialog.about.website = Website + +label.dialog.aspect.width = Width: +label.dialog.aspect.height = Height: + +label.dialog.speed.msPerTick = Milliseconds per tick: +label.dialog.speed.ticksPerSecond = Ticks per second: + +label.dialog.tunnel = Set channel name/code +label.dialog.tunnel.random = Generate random channel name +label.dialog.tunnel.copy = Copy channel name to the clipboard + +label.dialog.updates.checking = Checking development news... +label.dialog.updates.release = Release +label.dialog.updates.upToDate = Up to date +label.dialog.updates.releaseUpdateAvailable = New release version is available! +label.dialog.updates.devBuild = Dev build +label.dialog.updates.devUpdateAvailable = New dev build from +label.dialog.updates.unavailable = Ocelot website is unavailable... +label.dialog.updates.checkLogs = Check the log file for a full stacktrace. + +# Notifications +notification.saveBeforeExit = Save workspace before exiting? +notification.saveBeforeOpeningNew = Save workspace before opening a new one? + +# Ocelot error messages +error.ocelot.cannotOpenSpecifiedWorkspace = Could not open the specified workspace... +error.ocelot.cannotOpenRecentWorkspace = Could not open the recent workspace... +error.ocelot.defaultWorkspaceCreation = I will create a default one +error.ocelot.somethingWentWrong = Something went wrong! +error.ocelot.checkTheLogs = Check the log file for a full stacktrace. +error.ocelot.savePathNotEmpty = Chosen save path is not empty.[nl]Files in the save directory will be included in the workspace.[nl]They may be overwritten, causing loss of data.[nl]Proceed with saving anyway? +error.ocelot.overriding = You are overriding an existing file! + +# Computer error codes +Error.NoCPU = No CPU is installed in the computer. +Error.ComponentOverflow = Too many components connected to the computer. +Error.NoRAM = No RAM is installed in the computer. +Error.OutOfMemory = Out of memory. +Error.InternalError = Internal error, please see the log file. This is probably a bug. + +# Tooltips +tooltip.address = Address +tooltip.components = Components +tooltip.bytes = bytes +tooltip.bit = bit +tooltip.dataCard.softLimit = Soft limit +tooltip.dataCard.softLimitExplanation = sec slowdown +tooltip.dataCard.hardLimit = Hard limit (fail) +tooltip.disk.missingFloppy = Floppy: none +tooltip.disk.floppy = Floppy +tooltip.disk.label = Label +tooltip.disk.sourcePath = Source path +tooltip.disk.managedMode = Mode: managed +tooltip.disk.unmanagedMode = Mode: unmanaged +tooltip.disk.capacity = Capacity +tooltip.eeprom.sourcePath = Source path +tooltip.eeprom.sourceUrl = Source URL +tooltip.eeprom.code = Code +tooltip.eeprom.data = Data +tooltip.eeprom.data.readonly = Readonly +tooltip.gpu.maxResolution = Max resolution +tooltip.gpu.maxColorDepth = Max color depth +tooltip.gpu.vram = VRAM +tooltip.tunnel.channel = Channel +tooltip.ram.capacity.infinite = Capacity: infinite +tooltip.ram.capacity = Capacity +tooltip.modem.openPortsNone = Open ports: none +tooltip.modem.openPorts = Open ports +tooltip.modem.maxOpenPorts = Max open ports +tooltip.redstone.wakeThreshold = Wake threshold +tooltip.selfDestruct.fuseNotSet = Fuse has not been set +tooltip.selfDestruct.boom = BOOM! +tooltip.tape.label = Label +tooltip.tape.ID = ID +tooltip.tape.size = Size +tooltip.tape.length = Length + +# Selector +selector.group.original = Original +selector.group.addons = Addons +selector.group.custom = Custom + +# Tiers +tier.one = Tier 1 +tier.oneHalf = Tier 1.5 +tier.two = Tier 2 +tier.twoHalf = Tier 2.5 +tier.three = Tier 3 +tier.threeHalf = Tier 3.5 +tier.creative = Creative + +# Directions +direction.up = Up +direction.down = Down +direction.top = Top +direction.bottom = Bottom +direction.left = Left +direction.right = Right +direction.front = Front +direction.back = Back + +# Colors +color.white = White +color.orange = Orange +color.magenta = Magenta +color.lightBlue = LightBlue +color.yellow = Yellow +color.lime = Lime +color.pink = Pink +color.gray = Gray +color.silver = Silver +color.cyan = Cyan +color.purple = Purple +color.blue = Blue +color.brown = Brown +color.green = Green +color.red = Red +color.black = Black + +# Components +component.APU = APU +component.bundledIO = Bundled I/O +component.cable = Cable + +component.camera = Camera +component.camera.notAvailable = +component.camera.flipHorizontally = Flip image horizontally +component.camera.flipVertically = Flip image horizontally +component.camera.unlimitedCallBudget = Unlimited call budget + +component.chest = Chest +component.colorfulLamp = Colorful Lamp +component.componentBus = Component Bus +component.computer = Computer Case +component.CPU = CPU +component.dataCard = Data Card +component.diskDrive = Disk Drive +component.EEPROM = EEPROM +component.filesystem.floppy = Floppy +component.filesystem.floppyDisk = Floppy Disk +component.filesystem.hdd = HDD +component.filesystem.hardDiskDrive = Hard Disk Drive +component.graphicsCard = Graphics Card +component.hologramProjector = Hologram Projector +component.internetCard = Internet Card +component.ironNoteBlock = Iron Note Block +component.microcontroller = Microcontroller +component.modem = Network Card +component.modem.wireless = Wireless Net. Card + +component.noteBlock = Note Block +component.noteBlock.bass = Bass (Wood) +component.noteBlock.snare = Snare Drum (Sand) +component.noteBlock.hat = Hi-hat (Glass) +component.noteBlock.basedrum = Bass Drum (Stone) +component.noteBlock.bell = Glockenspiel (Gold) +component.noteBlock.flute = Flute (Clay) +component.noteBlock.chime = Chimes (Packed Ice) +component.noteBlock.guitar = Guitar (Wool) +component.noteBlock.xylophone = Xylophone (Bone) +component.noteBlock.ironXylophone = Vibraphone (Iron) +component.noteBlock.cowBell = Cow Bell (Soul Sand) +component.noteBlock.didgeridoo = Didgeridoo (Pumpkin) +component.noteBlock.bit = Square Wave (Emerald) +component.noteBlock.banjo = Banjo (Hay Bale) +component.noteBlock.pling = Electric Piano (Glowstone) +component.noteBlock.harp = Harp + +component.ocelot.block = Ocelot Block +component.ocelot.card = Ocelot Card +component.openFmRadio = OpenFM Radio +component.rack = Rack +component.raid = Raid +component.ram.magical = Magical Memory (Creative) +component.ram = Memory +component.relay = Relay +component.redstoneIO = Redstone I/O +component.redstoneCard = Redstone Card +component.screen = Screen +component.selfDestructingCard = Self-Destructing Card +component.server = Server +component.soundCard = Sound Card +component.tape = Tape +component.tapeDrive = Tape Drive +component.tunnel = Linked Card diff --git a/src/main/resources/ocelot/desktop/lang/manifest b/src/main/resources/ocelot/desktop/lang/manifest new file mode 100644 index 0000000..8cef476 --- /dev/null +++ b/src/main/resources/ocelot/desktop/lang/manifest @@ -0,0 +1,2 @@ +en_US = English +ru_RU = Русский diff --git a/src/main/resources/ocelot/desktop/lang/ru_RU.lang b/src/main/resources/ocelot/desktop/lang/ru_RU.lang new file mode 100644 index 0000000..b76f014 --- /dev/null +++ b/src/main/resources/ocelot/desktop/lang/ru_RU.lang @@ -0,0 +1,359 @@ +# This is the Russian localization file. +# Use [nl] to for a line break. + + +# Buttons +button.ok = Ок +button.cancel = Отмена +button.yes = Да +button.no = Нет +button.proceed = Продолжить +button.download = Скачать +button.apply = Применить + +# Loading screen +label.loading.brain = Инициализация движка... +label.loading.gui = Инициализация интерфейса... +label.loading.resources = Загрузка ресурсов... +label.loading.workspace = Загрузка проекта... + +# Titles +title.profiler = Профайлер + +# Windows +label.window.computer = Компьютер +label.window.server = Сервер +label.window.microcontroller = Микроконтроллер +label.window.computer.toggleStats = Открыть статистику использования ресурсов +label.window.computer.componentUsage = Лимит компонентов +label.window.computer.memoryUsage = Оперативная память +label.window.computer.cpuUsage = Процессор +label.window.computer.callBudget = Бюджет вызовов + +label.window.disk.used = Занято +label.window.disk.capacity = Размер +label.window.disk.label = Метка +label.window.disk.makeReadOnly = Запретить запись +label.window.disk.make.managed = Файловый режим +label.window.disk.make.unmanaged = Блочный режим + +label.window.ocelot.messages = Сообщения +label.window.ocelot.limit = Количество (максимум) +label.window.ocelot.scrollToEnd = Листать в конец +label.window.ocelot.clear = Очистить + +label.window.rack.enabled = Включено +label.window.rack.disabled = Выключено + +label.window.raid.warning.addingDisk = При добавлении диск очищается. +label.window.raid.warning.removingDisk = При удалении диска стирается весь массив. + +label.window.radio.screenColor = Цвет экрана +label.window.radio.screenText = Текст на экране + +label.window.relay.cycleRate = Цикличность +label.window.relay.packetsPerCycle = Пакеты / цикл +label.window.relay.queueSize = Размер очереди + +label.window.tapeDrive.unnamedTape = Безымянная кассета +label.window.tapeDrive.noTape = Нет кассеты + +# Menu +label.menu.file = Файл +label.menu.file.new = Новый +label.menu.file.open = Открыть +label.menu.file.save = Сохранить +label.menu.file.saveAs = Сохранить как +label.menu.file.exit = Выход + +label.menu.player = Игрок +label.menu.player.new = Создать +label.menu.player.remove = Удалить + +label.menu.settings = Настройки + +label.menu.help = Помощь +label.menu.help.updates = Проверить обновления +label.menu.help.manual = Руководство +label.menu.help.about = О программе + +label.menu.copyAddress = Скопировать адрес +label.menu.disconnect = Отсоединить +label.menu.remove = Удалить +label.menu.turnOn = Включить +label.menu.turnOff = Выключить +label.menu.reboot = Перезагрузить +label.menu.setLabel = Добавить метку +label.menu.ejectFloppy = Извлечь дискету +label.menu.changeFloppy = Поменять дискету +label.menu.setFloppy = Установить дискету +label.menu.changeSimulationSpeed = Изменить скорость симуляции +label.menu.resetSimulationSpeed = Сбросить скорость симуляции +label.menu.setup = Настроить +label.menu.instrument = Инструмент +label.menu.setAspectRatio = Изменить соотношение сторон +label.menu.removeKeyboard = Удалить клавиатуру +label.menu.addKeyboard = Добавить клавиатуру +label.menu.changeTier = Изменить уровень +label.menu.eject = Извлечь +label.menu.setArchitecture = Задать архитектуру +label.menu.currentArchitecture = текущая +label.menu.externalDataSource = Внешний источник данных +label.menu.localFile = Локальный файл +label.menu.fileViaUrl = Файл через URL +label.menu.detach = Сбросить +label.menu.setDirectory = Указать папку +label.menu.changeDirectory = Изменить папку +label.menu.resetDirectory = Сбросить папку +label.menu.editDisk = Редактировать диск +label.menu.setChannel = Изменить канал +label.menu.openConsole = Открыть консоль +label.menu.redstoneIO = Сигналы красного камня +label.menu.bundledIO = Цветные сигналы +label.menu.openCardInterface = Открыть интерфейс карты + +# Status bar +label.bar.menu = Меню +label.bar.addNode = Добавить блок +label.bar.moveNode = Передвинуть блок +label.bar.connectDisconnect = Присоединить/Отсоединить +label.bar.turnServerOnOff = Включить/выключить сервер +label.bar.playSample = Послушать образец +label.bar.open = Открыть +label.bar.close = Закрыть +label.bar.closeWindow = Закрыть окно +label.bar.rotateView = Вращать камеру +label.bar.panView = Перемещать камеру +label.bar.resetCamera = Вернуться в центр +label.bar.showGrid = Показать сетку +label.bar.closeSelector = Закрыть меню блоков +label.bar.moveCamera = Перемещаться по проекту +label.bar.scaleScreen = Уменьшить экран +label.bar.scaleScreenMagnify = Увеличить экран + +# Settings +label.settings.ui = Интерфейс +label.settings.ui.language = Язык: +label.settings.ui.pinWindows = Закреплять новые открытые окна +label.settings.ui.fullscreen = Полноэкранный режим +label.settings.ui.saveOnExit = Сохранять проект при выходе +label.settings.ui.openLastWorkspace = Открыть последний проект при запуске Ocelot +label.settings.ui.screenPreview = Показывать превью на блоках экрана +label.settings.ui.mipmaps = Использовать MIP-текстурирование для экранов +label.settings.ui.festiveDecorations = Включить праздничные декорации +label.settings.ui.scale = Масштаб интерфейса +label.settings.ui.tooltipDelay = Таймер подсказок: +label.settings.ui.tooltip.items = На предметах +label.settings.ui.tooltip.ui = В интерфейсе + +label.settings.sound = Звук +label.settings.sound.master = Общая громкость +label.settings.sound.music = Громкость нотных блоков +label.settings.sound.environment = Громкость фоновых звуков +label.settings.sound.beep = Громкость спикера +label.settings.sound.ui = Громкость интерфейса +label.settings.sound.positional = Использовать позиционный звук + +label.settings.system = Система +label.settings.system.confPath = Задать путь к файлу конфигурации OpenComputers: +label.settings.system.confPathPlaceholder = не задан / используется конфигурация по-умолчанию +label.settings.system.confSearch = Искать файл конфигурации OpenComputers +label.settings.system.confGenerate = Сгенерировать новый файл конфигурации OpenComputers в: +label.settings.system.currentDir = Текущей папке +label.settings.system.configDir = Папке с конфигом +label.settings.system.customDir = Выбрать +label.settings.system.autosave = Авто сохранение проекта +label.settings.system.autosavePeriod = Период авто сохранения (в секундах): +label.settings.system.restart = Перезапустите Ocelot чтобы применить новую конфигурацию + +# Dialogs +label.dialog.addNewPlayer = Добавить нового игрока + +label.dialog.about.subtitle = Эмулятор OpenComputers +label.dialog.about.version = Версия +label.dialog.about.website = Вебсайт + +label.dialog.aspect.width = Ширина: +label.dialog.aspect.height = Высота: + +label.dialog.speed.msPerTick = Миллисекунд в тик: +label.dialog.speed.ticksPerSecond = Тик в секунду: + +label.dialog.tunnel = Изменить название/код канала +label.dialog.tunnel.random = Сгенерировать случайное название +label.dialog.tunnel.copy = Скопировать название + +label.dialog.updates.checking = Проверяем новости разработки... +label.dialog.updates.release = Релиз +label.dialog.updates.upToDate = Последняя версия +label.dialog.updates.releaseUpdateAvailable = Доступен новый релиз! +label.dialog.updates.devBuild = Бета +label.dialog.updates.devUpdateAvailable = Новая бета от +label.dialog.updates.unavailable = Вебсайт Ocelot недоступен... +label.dialog.updates.checkLogs = Полный стек-трейс можно найти в логах. + +# Notifications +notification.saveBeforeExit = Сохранить проект перед выходом? +notification.saveBeforeOpeningNew = Сохранить проект прежде чем открыть новый? + +# Ocelot error messages +error.ocelot.cannotOpenSpecifiedWorkspace = Не могу открыть указанный проект... +error.ocelot.cannotOpenRecentWorkspace = Не могу открыть последний проект... +error.ocelot.defaultWorkspaceCreation = Я создам новый +error.ocelot.somethingWentWrong = Что-то пошло не так! +error.ocelot.checkTheLogs = Полный стек-трейс можно найти в логах. +error.ocelot.savePathNotEmpty = Выбранная папка не пуста.[nl]Файлы в этой папке будут включены в проект.[nl]Конфликтующие файлы могут быть утеряны.[nl]Продолжить сохранение? +error.ocelot.overriding = Вы пытаетесь перезаписать существующий файл! + +# Computer error codes +Error.NoCPU = Не установлен процессор. +Error.ComponentOverflow = Слишком много компонентов подключено к компьютеру. +Error.NoRAM = Не установлена оперативная память. +Error.OutOfMemory = Не хватает оперативной памяти. +Error.InternalError = Внутренняя ошибка. Пожалуйста проверьте логи. Возможно это баг. + +# Tooltips +tooltip.address = Адрес +tooltip.components = Компоненты +tooltip.bytes = байт +tooltip.bit = бит +tooltip.dataCard.softLimit = Мягкий лимит +tooltip.dataCard.softLimitExplanation = сек замедление +tooltip.dataCard.hardLimit = Жёсткий лимит (отказ) +tooltip.disk.missingFloppy = Дискета: нет +tooltip.disk.floppy = Дискета +tooltip.disk.label = Метка +tooltip.disk.sourcePath = Источник +tooltip.disk.managedMode = Режим: файловый +tooltip.disk.unmanagedMode = Режим: блочный +tooltip.disk.capacity = Размер +tooltip.eeprom.sourcePath = Источник +tooltip.eeprom.sourceUrl = Источник +tooltip.eeprom.code = Код +tooltip.eeprom.data = Данные +tooltip.eeprom.data.readonly = Только чтение +tooltip.gpu.maxResolution = Макс. разрешение +tooltip.gpu.maxColorDepth = Макс. глубина цвета +tooltip.gpu.vram = Видеопамять +tooltip.tunnel.channel = Канал +tooltip.ram.capacity.infinite = Объём: бесконечный +tooltip.ram.capacity = Объём +tooltip.modem.openPortsNone = Открытые порты: нет +tooltip.modem.openPorts = Открытые порты +tooltip.modem.maxOpenPorts = Макс. открытых портов +tooltip.redstone.wakeThreshold = Порог пробуждения +tooltip.selfDestruct.fuseNotSet = Фитиль не подожжён +tooltip.selfDestruct.boom = БУМ! +tooltip.tape.label = Метка +tooltip.tape.ID = ID +tooltip.tape.size = Размер +tooltip.tape.length = Длина + +# Selector +selector.group.original = Ванильные +selector.group.addons = Дополнения +selector.group.custom = Экстра + +# Tiers +tier.one = Уровень 1 +tier.oneHalf = Уровень 1.5 +tier.two = Уровень 2 +tier.twoHalf = Уровень 2.5 +tier.three = Уровень 3 +tier.threeHalf = Уровень 3.5 +tier.creative = Креативный + +# Directions +direction.up = Вверх +direction.down = Вниз +direction.top = Верхняя +direction.bottom = Нижняя +direction.left = Левая +direction.right = Правая +direction.front = Передняя +direction.back = Задняя + +# Colors +color.white = Белая +color.orange = Оранжевая +color.magenta = Пурпурная +color.lightBlue = Светло-синяя +color.yellow = Жёлтая +color.lime = Лаймовая +color.pink = Розовая +color.gray = Серая +color.silver = Светло-серая +color.cyan = Бирюзовая +color.purple = Фиолетовая +color.blue = Синяя +color.brown = Коричневая +color.green = Зелёная +color.red = Красная +color.black = Чёрная + +# Components +component.APU = APU +component.bundledIO = Цветной контроллер +component.cable = Кабель + +component.camera = Камера +component.camera.notAvailable = <Веб-камера недоступна> +component.camera.flipHorizontally = Отразить горизонтально +component.camera.flipVertically = Отразить вертикально +component.camera.unlimitedCallBudget = Неограниченный бюджет вызовов + +component.chest = Сундук +component.colorfulLamp = Цветная лампа +component.componentBus = Шина компонентов +component.computer = Корпус компьютера +component.CPU = CPU +component.dataCard = Карта данных +component.diskDrive = Дисковод +component.EEPROM = EEPROM +component.filesystem.floppy = Дискета +component.filesystem.floppyDisk = Дискета +component.filesystem.hdd = HDD +component.filesystem.hardDiskDrive = Жёсткий диск +component.graphicsCard = Видеокарта +component.hologramProjector = Голографический проектор +component.internetCard = Интернет карта +component.ironNoteBlock = Железный нотный блок +component.microcontroller = Микроконтроллер +component.modem = Сетевая карта +component.modem.wireless = Плата беспроводной сети + +component.noteBlock = Нотный блок +component.noteBlock.bass = Бас-гитара (Дерево) +component.noteBlock.snare = Малый барабан (Песок) +component.noteBlock.hat = Палочки (Стекло) +component.noteBlock.basedrum = Большой барабан (Булыжник) +component.noteBlock.bell = Колокольчики (Золото) +component.noteBlock.flute = Флейта (Глина) +component.noteBlock.chime = Бар чаймс (Плотный лёд) +component.noteBlock.guitar = Гитара (Шерсть) +component.noteBlock.xylophone = Ксилофон (Кость) +component.noteBlock.ironXylophone = Вибрафон (Железо) +component.noteBlock.cowBell = Коровий колокольчик (Песок душ) +component.noteBlock.didgeridoo = Диджериду (Тыква) +component.noteBlock.bit = Квадратная волна (Изумруд) +component.noteBlock.banjo = Банджо (Сноп сена) +component.noteBlock.pling = Электронное пианино (Светокамень) +component.noteBlock.harp = Пианино / Арфа + +component.ocelot.block = Блок Ocelot +component.ocelot.card = Ocelot карта +component.openFmRadio = Радио OpenFM +component.rack = Серверная стойка +component.raid = RAID +component.ram.magical = Магическая память (Креатив) +component.ram = Оперативная память +component.relay = Ретранслятор +component.redstoneIO = Красный контроллер +component.redstoneCard = Плата на красном камне +component.screen = Монитор +component.selfDestructingCard = Карта саморазрушения +component.server = Сервер +component.soundCard = Звуковая карта +component.tape = Кассета +component.tapeDrive = Стример +component.tunnel = Соединённая карта diff --git a/src/main/resources/ocelot/desktop/ocelot.conf b/src/main/resources/ocelot/desktop/ocelot.conf index 4d0e505..01a51a9 100644 --- a/src/main/resources/ocelot/desktop/ocelot.conf +++ b/src/main/resources/ocelot/desktop/ocelot.conf @@ -2,6 +2,11 @@ # Try setting your syntax highlighting to Ruby, to help readability. At least # in Sublime Text that works really well. ocelot { + general { + # Language used for UI. Defaults to "en_US" if Ocelot cannot find specified lang file. + language: "en_US" + } + brain { # This is the path to OpenComputers configuration file, that Ocelot Brain can use # to apply the settings which can be applicable in emulation context (like `bufferChanges: false`). diff --git a/src/main/scala/ocelot/desktop/ColorScheme.scala b/src/main/scala/ocelot/desktop/ColorScheme.scala index 800b4a7..273f952 100644 --- a/src/main/scala/ocelot/desktop/ColorScheme.scala +++ b/src/main/scala/ocelot/desktop/ColorScheme.scala @@ -14,7 +14,7 @@ class ColorScheme extends Logging { def add(key: String, color: RGBAColorNorm): Unit = entries.addOne((key, color)) def load(source: Source): Unit = { - logger.info(s"Loading color scheme") + logger.info(s"Loading color scheme...") val oldSize = entries.size diff --git a/src/main/scala/ocelot/desktop/OcelotDesktop.scala b/src/main/scala/ocelot/desktop/OcelotDesktop.scala index dd6c482..eb75842 100644 --- a/src/main/scala/ocelot/desktop/OcelotDesktop.scala +++ b/src/main/scala/ocelot/desktop/OcelotDesktop.scala @@ -8,6 +8,7 @@ import ocelot.desktop.ui.swing.SplashScreen import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.modal.notification.{NotificationDialog, NotificationType} import ocelot.desktop.util.CommandLine.Argument +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util._ import org.apache.commons.io.FileUtils import org.apache.commons.lang3.SystemUtils @@ -100,10 +101,10 @@ object OcelotDesktop extends LoggingConfiguration with Logging { Settings.load(desktopConfigPath) - Messages.load(Source.fromURL(getClass.getResource("/ocelot/desktop/messages.txt"))) + Messages.load(Settings.get.language) ColorScheme.load(Source.fromURL(getClass.getResource("/ocelot/desktop/colorscheme.txt"))) - splashScreen.setStatus("Initializing brain...", 0.20f) + splashScreen.setStatus(m"label.loading.brain", 0.20f) Ocelot.configPath = Settings.get.brainCustomConfigPath.map(Paths.get(_)) Ocelot.librariesPath = Some(OcelotPaths.libraries) Ocelot.isPlayerOnlinePredicate = Some(player => players.exists(_.nickname == player)) @@ -111,12 +112,12 @@ object OcelotDesktop extends LoggingConfiguration with Logging { Items.init() - splashScreen.setStatus("Initializing GUI...", 0.30f) + splashScreen.setStatus(m"label.loading.gui", 0.30f) createWorkspace() UiHandler.loadLibraries() - splashScreen.setStatus("Loading resources...", 0.60f) + splashScreen.setStatus(m"label.loading.resources", 0.60f) UiHandler.init() val loadRecentWorkspace = Settings.get.recentWorkspace.isDefined && Settings.get.openLastWorkspace @@ -125,7 +126,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging { root.size = Size2D(Display.getWidth / Settings.get.scaleFactor, Display.getHeight / Settings.get.scaleFactor) UiHandler.setRoot(root) - splashScreen.setStatus("Loading workspace...", 0.90f) + splashScreen.setStatus(m"label.loading.workspace", 0.90f) val cmdLineWorkspaceArgument = args.get(CommandLine.WorkspacePath).flatten if (loadRecentWorkspace || cmdLineWorkspaceArgument.isDefined) { val result = cmdLineWorkspaceArgument @@ -137,14 +138,15 @@ object OcelotDesktop extends LoggingConfiguration with Logging { result match { case Failure(exception) => - val errorMessage = if (cmdLineWorkspaceArgument.isDefined) - "Could not open the specified workspace..." - else - "Could not open the recent workspace..." + val errorMessage = if (cmdLineWorkspaceArgument.isDefined) { + logger.error("Could not open the specified workspace...", exception) + "error.ocelot.cannotOpenSpecifiedWorkspace" + } else { + logger.error("Could not open the recent workspace...", exception) + "error.ocelot.cannotOpenRecentWorkspace" + } - logger.error(errorMessage, exception) - - new NotificationDialog(s"$errorMessage\n($exception)\nI will create a default one", NotificationType.Info) + new NotificationDialog(s"${Message(errorMessage)}\n($exception)\n${m"error.ocelot.defaultWorkspaceCreation"}", NotificationType.Info) .addCloseButton() .show() @@ -281,7 +283,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging { } })) - def newWorkspace(): Unit = showCloseConfirmationDialog("Save workspace before opening a new one?") { + def newWorkspace(): Unit = showCloseConfirmationDialog(m"notification.saveBeforeOpeningNew") { root.workspaceView.newWorkspace() savePath = None Settings.get.recentWorkspace = None @@ -428,13 +430,13 @@ object OcelotDesktop extends LoggingConfiguration with Logging { logger.error("File operation failed", exception) new NotificationDialog( - s"Something went wrong!\n($exception)\nCheck the log file for a full stacktrace.", + s"${m"error.ocelot.somethingWentWrong"}\n($exception)\n${m"error.ocelot.checkTheLogs"}", NotificationType.Error, ).addCloseButton().show() } def showAddPlayerDialog(): Unit = new InputDialog( - "Add new player", + m"label.dialog.addNewPlayer", text => OcelotDesktop.selectPlayer(text), ).show() @@ -481,17 +483,11 @@ object OcelotDesktop extends LoggingConfiguration with Logging { } if (nonEmpty) { - new NotificationDialog( - """Chosen save path is not empty. - |Files in the save directory will be included in the workspace. - |They may be overwritten, causing loss of data. - |Proceed with saving anyway?""".stripMargin, - NotificationType.Warning, - ) { - addButton("Cancel") { + new NotificationDialog(m"error.ocelot.savePathNotEmpty", NotificationType.Warning) { + addButton(m"button.cancel") { close() } - addButton("Yes") { + addButton(m"button.yes") { close() continuation } @@ -522,7 +518,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging { } } - private def showCloseConfirmationDialog(prompt: Option[String])(continuation: => Unit): Unit = { + private def showCloseConfirmationDialog(prompt: Option[Message])(continuation: => Unit): Unit = { if (UiHandler.root.modalDialogPool.children.exists(_.isInstanceOf[CloseConfirmationDialog])) { return } @@ -537,7 +533,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging { val prompt_ = prompt new CloseConfirmationDialog { - override def prompt: String = prompt_.getOrElse(super.prompt) + override def prompt: Message = prompt_.getOrElse(super.prompt) override def onSaveSelected(): Unit = save { close() @@ -551,7 +547,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging { }.show() } - private def showCloseConfirmationDialog(prompt: String)(continuation: => Unit): Unit = + private def showCloseConfirmationDialog(prompt: Message)(continuation: => Unit): Unit = showCloseConfirmationDialog(Some(prompt))(continuation) private def showCloseConfirmationDialog()(continuation: => Unit): Unit = diff --git a/src/main/scala/ocelot/desktop/Settings.scala b/src/main/scala/ocelot/desktop/Settings.scala index a2e6b95..d1f7ce4 100644 --- a/src/main/scala/ocelot/desktop/Settings.scala +++ b/src/main/scala/ocelot/desktop/Settings.scala @@ -2,7 +2,7 @@ package ocelot.desktop import com.typesafe.config.{Config, ConfigFactory, ConfigRenderOptions, ConfigValueFactory} import ocelot.desktop.Settings.ExtendedConfig -import ocelot.desktop.util.{Logging, SettingsData} +import ocelot.desktop.util.{Language, Logging, SettingsData} import org.apache.commons.lang3.SystemUtils import java.io.InputStream @@ -45,6 +45,9 @@ class Settings(val config: Config) extends SettingsData { } recentWorkspace = config.getOptionalString("ocelot.workspace.recent") + + language = config.getOptionalString("ocelot.general.language").getOrElse(Language.DefaultCode) + pinNewWindows = config.getBooleanOrElse("ocelot.workspace.pinNewWindows", default = true) unfocusedWindowTransparency = config.getDoubleOrElse("ocelot.workspace.unfocusedWindowTransparency", 0.5) saveOnExit = config.getBooleanOrElse("ocelot.workspace.saveOnExit", default = true) @@ -187,6 +190,7 @@ object Settings extends Logging { .withValuePreserveOrigin("ocelot.window.disableVsync", settings.disableVsync) .withValuePreserveOrigin("ocelot.window.debugLwjgl", settings.debugLwjgl) .withValue("ocelot.workspace.recent", settings.recentWorkspace) + .withValuePreserveOrigin("ocelot.general.language", settings.language) .withValuePreserveOrigin("ocelot.workspace.pinNewWindows", settings.pinNewWindows) .withValuePreserveOrigin("ocelot.workspace.unfocusedWindowTransparency", settings.unfocusedWindowTransparency) .withValuePreserveOrigin("ocelot.workspace.saveOnExit", settings.saveOnExit) diff --git a/src/main/scala/ocelot/desktop/graphics/IconSource.scala b/src/main/scala/ocelot/desktop/graphics/IconSource.scala index ecb83c1..9d00c1c 100644 --- a/src/main/scala/ocelot/desktop/graphics/IconSource.scala +++ b/src/main/scala/ocelot/desktop/graphics/IconSource.scala @@ -9,6 +9,7 @@ import totoro.ocelot.brain.util.Tier.Tier case class IconSource( path: String, + tierTint: Boolean = false, animation: Option[IconSource.Animation] = None, ) @@ -207,7 +208,7 @@ object IconSource { object Computer extends PowerIconSource with DiskActivityIconSource { override protected def prefix: String = "nodes/computer" - val Default: IconSource = IconSource(s"$prefix/Default") + val Default: IconSource = IconSource(s"$prefix/Default", tierTint = true) } object DiskDrive extends DiskActivityIconSource with FloppyDriveIconSource { @@ -276,7 +277,7 @@ object IconSource { object Screen { protected val prefix: String = "nodes/screen" - val Standalone: IconSource = IconSource(s"$prefix/Standalone") + val Standalone: IconSource = IconSource(s"$prefix/Standalone", tierTint = true) val PowerOnOverlay: IconSource = IconSource(s"$prefix/PowerOnOverlay") val ColumnTop: IconSource = IconSource(s"$prefix/ColumnTop") diff --git a/src/main/scala/ocelot/desktop/graphics/ShaderProgram.scala b/src/main/scala/ocelot/desktop/graphics/ShaderProgram.scala index 985c372..acf74a4 100644 --- a/src/main/scala/ocelot/desktop/graphics/ShaderProgram.scala +++ b/src/main/scala/ocelot/desktop/graphics/ShaderProgram.scala @@ -8,7 +8,7 @@ import org.lwjgl.opengl.GL20 import scala.io.Source class ShaderProgram(name: String) extends Logging with Resource { - logger.info(s"Loading shader program ($name)") + logger.info(s"Loading shader program ($name)...") private val fragmentShader: Int = createShader( Source.fromResource(s"ocelot/desktop/shader/$name.frag", getClass.getClassLoader), diff --git a/src/main/scala/ocelot/desktop/inventory/Item.scala b/src/main/scala/ocelot/desktop/inventory/Item.scala index 22d9481..018efaa 100644 --- a/src/main/scala/ocelot/desktop/inventory/Item.scala +++ b/src/main/scala/ocelot/desktop/inventory/Item.scala @@ -7,7 +7,7 @@ import ocelot.desktop.ui.event.EventAware import ocelot.desktop.ui.widget.Updatable import ocelot.desktop.ui.widget.contextmenu.ContextMenu import ocelot.desktop.ui.widget.tooltip.ItemTooltip -import ocelot.desktop.util.Disposable +import ocelot.desktop.util.{Disposable, Message} import totoro.ocelot.brain.util.Tier import totoro.ocelot.brain.util.Tier.Tier @@ -49,7 +49,7 @@ trait Item extends EventAware with Updatable with Disposable { protected def onRemoved(): Unit = {} /** The name of the item (as shown in the tooltip). */ - def name: String = factory.name + def name: Message = factory.name.toString /** The icon used to draw the item in a slot. */ def icon: IconSource = factory.icon diff --git a/src/main/scala/ocelot/desktop/inventory/ItemFactory.scala b/src/main/scala/ocelot/desktop/inventory/ItemFactory.scala index fb369d4..0659f01 100644 --- a/src/main/scala/ocelot/desktop/inventory/ItemFactory.scala +++ b/src/main/scala/ocelot/desktop/inventory/ItemFactory.scala @@ -1,6 +1,7 @@ package ocelot.desktop.inventory import ocelot.desktop.graphics.IconSource +import ocelot.desktop.util.Message import totoro.ocelot.brain.util.Tier.Tier /** Provides information about a class of [[Item]]s and allows to build an item instance. @@ -27,7 +28,7 @@ trait ItemFactory { * * Used by the [[ocelot.desktop.ui.widget.slot.ItemChooser ItemChooser]] in its entries. */ - def name: String + def name: Message /** The tier of an item this factory will construct in its [[build]] method. * diff --git a/src/main/scala/ocelot/desktop/inventory/Items.scala b/src/main/scala/ocelot/desktop/inventory/Items.scala index ed09fbd..5f3be00 100644 --- a/src/main/scala/ocelot/desktop/inventory/Items.scala +++ b/src/main/scala/ocelot/desktop/inventory/Items.scala @@ -35,7 +35,7 @@ object Items extends Logging { } def registerSingleton(factory: ItemFactory): Unit = { - _groups += SingletonItemGroup(factory.name, factory) + _groups += SingletonItemGroup(factory.name.toString, factory) registerItemFactoryRecoverers(factory) } @@ -108,7 +108,7 @@ object Items extends Logging { case ExtendedTier.Creative => new MagicalMemoryItem.Factory() case tier => new MemoryItem.Factory(tier) } - .map(factory => (factory.name, factory)), + .map(factory => (factory.name.toString, factory)), ) registerTiered("HDD", Tier.One to Tier.Three)(new HddItem.Factory(managed = true, _)) @@ -118,7 +118,7 @@ object Items extends Logging { FloppyItem.Factory.Empty.icon, Loot.Floppies.iterator .map(new FloppyItem.Factory.Loot(_)) - .map(factory => (factory.name, factory)) ++ Some(("Empty", FloppyItem.Factory.Empty)), + .map(factory => (factory.name.toString, factory)) ++ Some(("Empty", FloppyItem.Factory.Empty)), ) registerArbitrary( @@ -126,7 +126,7 @@ object Items extends Logging { EepromItem.Factory.Empty.icon, Loot.Eeproms.iterator .map(new EepromItem.Factory.Loot(_)) - .map(factory => (factory.name, factory)) ++ Some(("Empty", EepromItem.Factory.Empty)), + .map(factory => (factory.name.toString, factory)) ++ Some(("Empty", EepromItem.Factory.Empty)), ) registerTiered("Graphics Card", Tier.One to Tier.Three)(new GraphicsCardItem.Factory(_)) diff --git a/src/main/scala/ocelot/desktop/inventory/item/ApuItem.scala b/src/main/scala/ocelot/desktop/inventory/item/ApuItem.scala index 012e07a..94d0316 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/ApuItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/ApuItem.scala @@ -3,6 +3,8 @@ package ocelot.desktop.inventory.item import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.{ComponentItem, CpuLikeItem, GpuLikeItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.APU import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU, GenericGPU} import totoro.ocelot.brain.util.Tier.Tier @@ -19,7 +21,7 @@ object ApuItem { override def itemClass: Class[I] = classOf - override def name: String = s"APU (${tier.get.label})" + override def name: Message = s"${m"component.APU"} (${m"${tier.get.label}"})" // there are T2, T3, and creative APUs // however, the APU class starts counting from one (so it's actually T1, T2, and T3) diff --git a/src/main/scala/ocelot/desktop/inventory/item/ComponentBusItem.scala b/src/main/scala/ocelot/desktop/inventory/item/ComponentBusItem.scala index bd7c704..6bae47c 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/ComponentBusItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/ComponentBusItem.scala @@ -4,6 +4,8 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.EntityItem import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.ComponentBus import totoro.ocelot.brain.util.Tier.Tier @@ -14,7 +16,7 @@ class ComponentBusItem(val componentBus: ComponentBus) extends Item with EntityI override def fillTooltip(tooltip: ItemTooltip): Unit = { super.fillTooltip(tooltip) if (componentBus != null) { - tooltip.addLine(s"Components: ${componentBus.supportedComponents}") + tooltip.addLine(s"${m"tooltip.components"}: ${componentBus.supportedComponents}") } } } @@ -25,7 +27,7 @@ object ComponentBusItem { override def itemClass: Class[I] = classOf - override def name: String = s"Component Bus (${_tier.label})" + override def name: Message = s"${m"component.componentBus"} (${m"${_tier.label}"})" override def tier: Option[Tier] = Some(_tier) diff --git a/src/main/scala/ocelot/desktop/inventory/item/CpuItem.scala b/src/main/scala/ocelot/desktop/inventory/item/CpuItem.scala index 44b5668..e3d89f3 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/CpuItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/CpuItem.scala @@ -3,6 +3,8 @@ package ocelot.desktop.inventory.item import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.{ComponentItem, CpuLikeItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.CPU import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU} import totoro.ocelot.brain.util.Tier.Tier @@ -19,7 +21,7 @@ object CpuItem { override def itemClass: Class[I] = classOf - override def name: String = s"CPU (${_tier.label})" + override def name: Message = s"${m"component.CPU"} (${m"${_tier.label}"})" override def tier: Option[Tier] = Some(_tier) diff --git a/src/main/scala/ocelot/desktop/inventory/item/DataCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/DataCardItem.scala index 49a67f5..5149df9 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/DataCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/DataCardItem.scala @@ -4,6 +4,8 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.Settings import totoro.ocelot.brain.entity.DataCard import totoro.ocelot.brain.entity.traits.{Entity, Environment} @@ -14,15 +16,15 @@ abstract class DataCardItem extends Item with ComponentItem with PersistableItem override def fillTooltip(tooltip: ItemTooltip): Unit = { super.fillTooltip(tooltip) tooltip.addLine( - s"Soft limit (${Settings.get.dataCardTimeout} sec slowdown): ${Settings.get.dataCardSoftLimit} bytes" + s"${m"tooltip.dataCard.softLimit"} (${Settings.get.dataCardTimeout} ${m"tooltip.dataCard.softLimitExplanation"}): ${Settings.get.dataCardSoftLimit} ${m"tooltip.bytes"}" ) - tooltip.addLine(s"Hard limit (fail): ${Settings.get.dataCardHardLimit} bytes") + tooltip.addLine(s"${m"tooltip.dataCard.hardLimit"}: ${Settings.get.dataCardHardLimit} ${m"tooltip.bytes"}") } } object DataCardItem { abstract class Factory extends ItemFactory { - override def name: String = s"Data Card (${tier.get.label})" + override def name: Message = s"${m"component.dataCard"} (${m"${tier.get.label}"})" override def icon: IconSource = IconSource.Items.DataCard(tier.get) } diff --git a/src/main/scala/ocelot/desktop/inventory/item/DiskDriveMountableItem.scala b/src/main/scala/ocelot/desktop/inventory/item/DiskDriveMountableItem.scala index 2da6072..07878e3 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/DiskDriveMountableItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/DiskDriveMountableItem.scala @@ -4,7 +4,8 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.RackMountableItem import ocelot.desktop.inventory.{ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.tooltip.ItemTooltip -import ocelot.desktop.util.DiskDriveAware +import ocelot.desktop.util.{DiskDriveAware, Message} +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.traits.Floppy import totoro.ocelot.brain.entity.{DiskDriveMountable, FloppyDiskDrive} import totoro.ocelot.brain.util.Tier.Tier @@ -19,8 +20,8 @@ class DiskDriveMountableItem(val diskDriveMountable: DiskDriveMountable) extends super.fillTooltip(tooltip) if (diskDriveMountable != null) { diskDriveMountable.inventory(0).get match { - case None => tooltip.addLine("Floppy: none") - case Some(floppy: Floppy) if floppy.name.isDefined => tooltip.addLine(s"Floppy: ${floppy.name.get}") + case None => tooltip.addLine(m"tooltip.disk.missingFloppy") + case Some(floppy: Floppy) if floppy.name.isDefined => tooltip.addLine(s"${m"tooltip.disk.floppy"}: ${floppy.name.get}") case _ => } } @@ -35,7 +36,7 @@ object DiskDriveMountableItem { override def tier: Option[Tier] = None - override def name: String = "Disk Drive" + override def name: Message = m"component.diskDrive" override def icon: IconSource = IconSource.Items.DiskDriveMountable diff --git a/src/main/scala/ocelot/desktop/inventory/item/EepromItem.scala b/src/main/scala/ocelot/desktop/inventory/item/EepromItem.scala index 727a06d..b1c3eab 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/EepromItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/EepromItem.scala @@ -7,6 +7,8 @@ import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.InputDialog import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuIcon, ContextMenuSubmenu} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.Settings import totoro.ocelot.brain.entity.EEPROM import totoro.ocelot.brain.entity.traits.{Entity, Environment} @@ -24,8 +26,8 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis super.fillTooltip(tooltip) val source = eeprom.codePath - .map(path => s"Source path: $path") - .orElse(eeprom.codeURL.map(url => s"Source URL: $url")) + .map(path => s"${m"tooltip.eeprom.sourcePath"}: $path") + .orElse(eeprom.codeURL.map(url => s"${m"tooltip.eeprom.sourceUrl"}: $url")) for (source <- source) { tooltip.addLine(source) @@ -34,19 +36,19 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis if (source.isEmpty && eeprom != null) { if (eeprom.codeBytes != null) { tooltip.addLine( - s"Code: ${eeprom.codeBytes.map(_.length).getOrElse(0)} bytes / ${Settings.get.eepromSize} bytes" + s"${m"tooltip.eeprom.code"}: ${eeprom.codeBytes.map(_.length).getOrElse(0)} ${m"tooltip.bytes"} / ${Settings.get.eepromSize} ${m"tooltip.bytes"}" ) } if (eeprom.volatileData != null) - tooltip.addLine(s"Data: ${eeprom.volatileData.length} bytes / ${Settings.get.eepromDataSize} bytes") + tooltip.addLine(s"${m"tooltip.eeprom.data"}: ${eeprom.volatileData.length} ${m"tooltip.bytes"} / ${Settings.get.eepromDataSize} ${m"tooltip.bytes"}") if (eeprom.readonly) - tooltip.addLine("Readonly") + tooltip.addLine(m"tooltip.eeprom.data.readonly") } } override def fillRmbMenu(menu: ContextMenu): Unit = { - menu.addEntry(new ContextMenuSubmenu("External data source", Some(ContextMenuIcon(IconSource.Code))) { - addEntry(ContextMenuEntry("Local file", IconSource.File) { + menu.addEntry(new ContextMenuSubmenu(m"label.menu.externalDataSource", Some(ContextMenuIcon(IconSource.Code))) { + addEntry(ContextMenuEntry(m"label.menu.localFile", IconSource.File) { OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.FILES_ONLY) { file => Try { for (file <- file) { @@ -56,9 +58,9 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis } }) - addEntry(ContextMenuEntry("File via URL", IconSource.Link) { + addEntry(ContextMenuEntry(m"label.menu.fileViaUrl", IconSource.Link) { new InputDialog( - title = "File via URL", + title = m"label.menu.fileViaUrl", onConfirmed = { text => eeprom.codeURL = Some(new URL(text)) }, @@ -74,7 +76,7 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis }) if (eeprom.codePath.nonEmpty || eeprom.codeURL.nonEmpty) { - addEntry(ContextMenuEntry("Detach", IconSource.LinkSlash) { + addEntry(ContextMenuEntry(m"label.menu.detach", IconSource.LinkSlash) { eeprom.codeBytes = Some(Array.empty) }) } @@ -105,16 +107,16 @@ object EepromItem { object Factory { class Loot(factory: LootEepromFactory) extends Factory { - override def name: String = factory.label + override def name: Message = factory.label override def build(): EepromItem = new EepromItem(factory.create()) } - class Code(code: Array[Byte], override val name: String, readonly: Boolean) extends Factory { + class Code(code: Array[Byte], override val name: Message, readonly: Boolean) extends Factory { override def build(): EepromItem = { val eeprom = new EEPROM eeprom.codeBytes = Some(code) - eeprom.label = name + eeprom.label = name.toString eeprom.readonly = readonly new EepromItem(eeprom) @@ -122,7 +124,7 @@ object EepromItem { } object Empty extends Factory { - override def name: String = "EEPROM" + override def name: Message = m"component.EEPROM" override def build(): EepromItem = new EepromItem(new EEPROM) } diff --git a/src/main/scala/ocelot/desktop/inventory/item/FloppyItem.scala b/src/main/scala/ocelot/desktop/inventory/item/FloppyItem.scala index 097ec56..126aa16 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/FloppyItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/FloppyItem.scala @@ -4,19 +4,21 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.{ComponentItem, DiskItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.fs.ReadWriteLabel import totoro.ocelot.brain.entity.traits.{Disk, Entity, Floppy} import totoro.ocelot.brain.entity.{FloppyManaged, FloppyUnmanaged} -import totoro.ocelot.brain.loot.Loot.{FloppyFactory => LootFloppyFactory, LootFloppy} +import totoro.ocelot.brain.loot.Loot.{LootFloppy, FloppyFactory => LootFloppyFactory} import totoro.ocelot.brain.util.DyeColor import totoro.ocelot.brain.util.Tier.Tier class FloppyItem(var floppy: Floppy) extends Item with ComponentItem with PersistableItem with DiskItem { override def entity: Entity with Disk = floppy - override def diskKind: String = "Floppy" + override def diskKind: Message = m"component.filesystem.floppy" - override def name: String = floppy.name.getOrElse(super.name) + override def name: Message = floppy.name.map(Message(_)).getOrElse(super.name) override def label: Option[String] = floppy.label.labelOption @@ -80,7 +82,7 @@ object FloppyItem { override def itemClass: Class[I] = classOf - override def name: String = label.getOrElse("Floppy Disk") + override def name: Message = label.map(Message(_)).getOrElse(m"component.filesystem.floppyDisk") override def tier: Option[Tier] = None @@ -91,7 +93,7 @@ object FloppyItem { object Factory { class Loot(factory: LootFloppyFactory) extends Factory(Some(factory.name), factory.color, managed = true) { - override def name: String = factory.name + override def name: Message = factory.name override def build(): FloppyItem = new FloppyItem(factory.create()) } diff --git a/src/main/scala/ocelot/desktop/inventory/item/GraphicsCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/GraphicsCardItem.scala index c3590ef..b142a67 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/GraphicsCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/GraphicsCardItem.scala @@ -3,6 +3,8 @@ package ocelot.desktop.inventory.item import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, GpuLikeItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.GraphicsCard import totoro.ocelot.brain.entity.traits.{Entity, GenericGPU} import totoro.ocelot.brain.util.Tier.Tier @@ -20,7 +22,7 @@ object GraphicsCardItem { override def itemClass: Class[I] = classOf - override def name: String = s"Graphics Card (${_tier.label})" + override def name: Message = s"${m"component.graphicsCard"} (${m"${_tier.label}"})" override def tier: Option[Tier] = Some(_tier) override def icon: IconSource = IconSource.Items.GraphicsCard(_tier) diff --git a/src/main/scala/ocelot/desktop/inventory/item/HddItem.scala b/src/main/scala/ocelot/desktop/inventory/item/HddItem.scala index 87f1a61..9f17834 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/HddItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/HddItem.scala @@ -5,6 +5,8 @@ import ocelot.desktop.inventory.item.HddItem.Hdd import ocelot.desktop.inventory.traits.{ComponentItem, DiskItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.fs.{Label, ReadWriteLabel} import totoro.ocelot.brain.entity.traits.{Disk, Entity} import totoro.ocelot.brain.entity.{HDDManaged, HDDUnmanaged} @@ -22,7 +24,7 @@ class HddItem(var hdd: Hdd) extends Item with ComponentItem with PersistableItem override def entity: Entity with Disk = hdd.hdd - override def diskKind: String = "HDD" + override def diskKind: Message = m"component.filesystem.hdd" private def fsLabel: Label = hdd match { case Hdd.Managed(hdd) => hdd.fileSystem.label @@ -72,7 +74,7 @@ object HddItem { override def itemClass: Class[I] = classOf - override def name: String = s"Hard Disk Drive (${_tier.label})" + override def name: Message = s"${m"component.filesystem.hardDiskDrive"} (${m"${_tier.label}"})" override def tier: Option[Tier] = Some(_tier) diff --git a/src/main/scala/ocelot/desktop/inventory/item/InternetCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/InternetCardItem.scala index 359a1eb..511e566 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/InternetCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/InternetCardItem.scala @@ -3,6 +3,8 @@ package ocelot.desktop.inventory.item import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.InternetCard import totoro.ocelot.brain.entity.traits.{Entity, Environment} import totoro.ocelot.brain.util.Tier @@ -20,7 +22,7 @@ object InternetCardItem { override def itemClass: Class[InternetCardItem] = classOf - override def name: String = "Internet Card" + override def name: Message = m"component.internetCard" override def tier: Option[Tier] = Some(Tier.Two) diff --git a/src/main/scala/ocelot/desktop/inventory/item/LinkedCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/LinkedCardItem.scala index 6ccdda9..ad0014d 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/LinkedCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/LinkedCardItem.scala @@ -6,6 +6,8 @@ import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.TunnelDialog import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.LinkedCard import totoro.ocelot.brain.entity.traits.{Entity, Environment} import totoro.ocelot.brain.util.Tier @@ -17,12 +19,12 @@ class LinkedCardItem(val linkedCard: LinkedCard) extends Item with ComponentItem override def fillTooltip(tooltip: ItemTooltip): Unit = { super.fillTooltip(tooltip) - tooltip.addLine(s"Channel: ${linkedCard.tunnel}") + tooltip.addLine(s"${m"tooltip.tunnel.channel"}: ${linkedCard.tunnel}") } override def fillRmbMenu(menu: ContextMenu): Unit = { menu.addEntry( - ContextMenuEntry("Set channel", IconSource.Antenna) { + ContextMenuEntry(m"label.menu.setChannel", IconSource.Antenna) { new TunnelDialog( tunnel => linkedCard.tunnel = tunnel, linkedCard.tunnel, @@ -41,7 +43,7 @@ object LinkedCardItem { override def itemClass: Class[I] = classOf - override def name: String = "Linked Card" + override def name: Message = m"component.tunnel" override def tier: Option[Tier] = Some(Tier.Three) diff --git a/src/main/scala/ocelot/desktop/inventory/item/MagicalMemoryItem.scala b/src/main/scala/ocelot/desktop/inventory/item/MagicalMemoryItem.scala index 9cfaeab..281f9e4 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/MagicalMemoryItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/MagicalMemoryItem.scala @@ -3,7 +3,8 @@ package ocelot.desktop.inventory.item import ocelot.desktop.color.Color import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.{ItemFactory, ItemRecoverer} -import ocelot.desktop.util.TierColor +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, TierColor} import totoro.ocelot.brain.entity.MagicalMemory import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.{ExtendedTier, Tier} @@ -20,7 +21,7 @@ object MagicalMemoryItem { override def itemClass: Class[I] = classOf - override def name: String = "Magical Memory (Creative)" + override def name: Message = m"component.ram.magical" override def tier: Option[Tier] = Some(Tier.One) diff --git a/src/main/scala/ocelot/desktop/inventory/item/MemoryItem.scala b/src/main/scala/ocelot/desktop/inventory/item/MemoryItem.scala index 9431b15..5d719cd 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/MemoryItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/MemoryItem.scala @@ -4,6 +4,8 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.{ComponentItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.Memory import totoro.ocelot.brain.entity.traits.{Entity, Environment} import totoro.ocelot.brain.util.ExtendedTier.ExtendedTier @@ -17,8 +19,8 @@ class MemoryItem(val memory: Memory) extends Item with ComponentItem with Persis override def fillTooltip(tooltip: ItemTooltip): Unit = { super.fillTooltip(tooltip) tooltip.addLine( - if (memory.amount == Double.PositiveInfinity) "Capacity: infinite" - else s"Capacity: ${memory.amount.toInt} kB" + if (memory.amount == Double.PositiveInfinity) m"tooltip.ram.capacity.infinite" + else s"${m"tooltip.ram.capacity"}: ${memory.amount.toInt} kB" ) } } @@ -29,7 +31,7 @@ object MemoryItem { override def itemClass: Class[I] = classOf - override def name: String = s"Memory (${memoryTier.label})" + override def name: Message = s"${m"component.ram"} (${m"${memoryTier.label}"})" override def tier: Option[Tier] = Some(memoryTier.toTier) diff --git a/src/main/scala/ocelot/desktop/inventory/item/NetworkCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/NetworkCardItem.scala index 7cd41d6..54e4eda 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/NetworkCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/NetworkCardItem.scala @@ -4,6 +4,8 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.NetworkCard import totoro.ocelot.brain.entity.traits.Entity import totoro.ocelot.brain.util.Tier @@ -18,10 +20,10 @@ class NetworkCardItem(val card: NetworkCard) extends Item with ComponentItem wit super.fillTooltip(tooltip) if (card != null) { tooltip.addLine( - if (card.openPorts.isEmpty) s"Open ports: none" - else s"Open ports: ${card.openPorts.mkString(", ")}" + if (card.openPorts.isEmpty) m"tooltip.modem.openPortsNone" + else s"${m"tooltip.modem.openPorts"}: ${card.openPorts.mkString(", ")}" ) - tooltip.addLine(s"Max open ports: ${card.maxOpenPorts}") + tooltip.addLine(s"${m"tooltip.modem.maxOpenPorts"}: ${card.maxOpenPorts}") } } } @@ -32,7 +34,7 @@ object NetworkCardItem { override def itemClass: Class[I] = classOf - override def name: String = "Network Card" + override def name: Message = m"component.modem" override def tier: Option[Tier] = Some(Tier.One) override def icon: IconSource = IconSource.Items.NetworkCard diff --git a/src/main/scala/ocelot/desktop/inventory/item/OcelotCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/OcelotCardItem.scala index 69a6629..1962250 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/OcelotCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/OcelotCardItem.scala @@ -9,7 +9,8 @@ import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.tooltip.ItemTooltip -import ocelot.desktop.util.{Logging, OcelotInterfaceLogStorage} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Logging, Message, OcelotInterfaceLogStorage} import totoro.ocelot.brain.util.Tier import totoro.ocelot.brain.util.Tier.Tier @@ -23,7 +24,7 @@ class OcelotCardItem(val ocelotCard: OcelotCard) override def tooltipNameColor: Color = ColorScheme("OcelotCardTooltip") override def fillRmbMenu(menu: ContextMenu): Unit = { - menu.addEntry(ContextMenuEntry("Open console", IconSource.Window) { + menu.addEntry(ContextMenuEntry(m"label.menu.openConsole", IconSource.Window) { window.open() }) @@ -48,7 +49,7 @@ object OcelotCardItem { override def itemClass: Class[I] = classOf - override def name: String = "Ocelot Card" + override def name: Message = m"component.ocelot.card" override def tier: Option[Tier] = Some(Tier.One) diff --git a/src/main/scala/ocelot/desktop/inventory/item/RedstoneCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/RedstoneCardItem.scala index 29635dc..16a3f54 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/RedstoneCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/RedstoneCardItem.scala @@ -7,6 +7,8 @@ import ocelot.desktop.ui.widget.card.{Redstone1Window, Redstone2Window} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.tooltip.ItemTooltip import ocelot.desktop.ui.widget.window.Windowed +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.Redstone import totoro.ocelot.brain.entity.traits.{Entity, Environment} import totoro.ocelot.brain.nbt.NBTTagCompound @@ -17,7 +19,7 @@ abstract class RedstoneCardItem extends Item with ComponentItem with Persistable object RedstoneCardItem { abstract class Factory extends ItemFactory { - override def name: String = s"Redstone Card (${tier.get.label})" + override def name: Message = s"${m"component.redstoneCard"} (${m"${tier.get.label}"})" override def icon: IconSource = IconSource.Items.RedstoneCard(tier.get) } @@ -31,7 +33,7 @@ object RedstoneCardItem { super.fillTooltip(tooltip) if (redstoneCard.wakeThreshold > 0) - tooltip.addLine(s"Wake threshold: ${redstoneCard.wakeThreshold}") + tooltip.addLine(s"${m"tooltip.redstone.wakeThreshold"}: ${redstoneCard.wakeThreshold}") } // ----------------------------- Window ----------------------------- @@ -43,7 +45,7 @@ object RedstoneCardItem { } override def fillRmbMenu(menu: ContextMenu): Unit = { - menu.addEntry(ContextMenuEntry("Redstone I/O", IconSource.ArrowRight) { + menu.addEntry(ContextMenuEntry(m"label.menu.redstoneIO", IconSource.ArrowRight) { windowed.window.open() }) @@ -93,7 +95,7 @@ object RedstoneCardItem { } override def fillRmbMenu(menu: ContextMenu): Unit = { - menu.addEntry(ContextMenuEntry("Bundled I/O", IconSource.LinesHorizontal) { + menu.addEntry(ContextMenuEntry(m"label.menu.bundledIO", IconSource.LinesHorizontal) { windowed.window.open() }) diff --git a/src/main/scala/ocelot/desktop/inventory/item/SelfDestructingCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/SelfDestructingCardItem.scala index 380c87c..cef254c 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/SelfDestructingCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/SelfDestructingCardItem.scala @@ -4,6 +4,8 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.SelfDestructingCard import totoro.ocelot.brain.entity.traits.{Entity, Environment} import totoro.ocelot.brain.util.Tier @@ -20,8 +22,8 @@ class SelfDestructingCardItem(val card: SelfDestructingCard) super.fillTooltip(tooltip) if (card != null) { tooltip.addLine( - if (card.remainingTime < 0) "Fuse has not been set" - else if (card.remainingTime == 0) "BOOM!" + if (card.remainingTime < 0) m"tooltip.selfDestruct.fuseNotSet" + else if (card.remainingTime == 0) m"tooltip.selfDestruct.boom" else card.remainingTime.toString ) } @@ -34,7 +36,7 @@ object SelfDestructingCardItem { override def itemClass: Class[I] = classOf - override def name: String = "Self-Destructing Card" + override def name: Message = m"component.selfDestructingCard" override def tier: Option[Tier] = Some(Tier.Two) diff --git a/src/main/scala/ocelot/desktop/inventory/item/ServerItem.scala b/src/main/scala/ocelot/desktop/inventory/item/ServerItem.scala index dfdf032..bc6ca49 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/ServerItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/ServerItem.scala @@ -5,7 +5,8 @@ import ocelot.desktop.inventory.traits.RackMountableItem import ocelot.desktop.inventory.{ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.slot._ import ocelot.desktop.util.ComputerType.ComputerType -import ocelot.desktop.util.{AudibleComputerAware, ComputerType} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{AudibleComputerAware, ComputerType, Message} import totoro.ocelot.brain.entity.Server import totoro.ocelot.brain.entity.traits.Inventory import totoro.ocelot.brain.util.Tier @@ -103,7 +104,7 @@ object ServerItem { override def itemClass: Class[I] = classOf - override def name: String = s"Server (${_tier.label})" + override def name: Message = s"${m"component.server"} (${m"${_tier.label}"})" override def tier: Option[Tier] = Some(_tier) diff --git a/src/main/scala/ocelot/desktop/inventory/item/SoundCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/SoundCardItem.scala index a473d2a..68429a3 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/SoundCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/SoundCardItem.scala @@ -6,6 +6,8 @@ import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.card.SoundCardWindow import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.window.Windowed +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.sound_card.SoundCard import totoro.ocelot.brain.entity.traits.{Entity, Environment} import totoro.ocelot.brain.util.Tier @@ -19,7 +21,7 @@ class SoundCardItem(val soundCard: SoundCard) override def entity: Entity with Environment = soundCard override def fillRmbMenu(menu: ContextMenu): Unit = { - menu.addEntry(ContextMenuEntry("Open card interface", IconSource.Window) { + menu.addEntry(ContextMenuEntry(m"label.menu.openCardInterface", IconSource.Window) { window.open() }) @@ -37,7 +39,7 @@ object SoundCardItem { override def itemClass: Class[I] = classOf - override def name: String = "Sound Card" + override def name: Message = m"component.soundCard" override def tier: Option[Tier] = Some(Tier.Two) diff --git a/src/main/scala/ocelot/desktop/inventory/item/TapeItem.scala b/src/main/scala/ocelot/desktop/inventory/item/TapeItem.scala index ad0b6d9..56c7ad6 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/TapeItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/TapeItem.scala @@ -5,6 +5,8 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.EntityItem import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.SizeFormatting.formatSize import totoro.ocelot.brain.entity.tape.Tape import totoro.ocelot.brain.nbt.NBTTagCompound @@ -19,15 +21,15 @@ class TapeItem(var tape: Tape) extends Item with EntityItem { super.fillTooltip(tooltip) if (tape.label.nonEmpty) { - tooltip.addLine(s"Label: ${tape.label}") + tooltip.addLine(s"${m"tooltip.tape.label"}: ${tape.label}") } for (storageId <- tape.storageId) { - tooltip.addLine(s"ID: $storageId") + tooltip.addLine(s"${m"tooltip.tape.ID"}: $storageId") } - tooltip.addLine(s"Size: ${formatSize(tape.size)}") - tooltip.addLine(f"Length: ${tape.lengthMinutes}%.0f minutes") + tooltip.addLine(s"${m"tooltip.tape.size"}: ${formatSize(tape.size)}") + tooltip.addLine(f"${m"tooltip.tape.length"}: ${tape.lengthMinutes}%.0f minutes") } override def load(nbt: NBTTagCompound): Unit = { @@ -49,7 +51,7 @@ object TapeItem { override def itemClass: Class[TapeItem] = classOf - override def name: String = "Tape" + override def name: Message = m"component.tape" override def tier: Option[Tier] = None diff --git a/src/main/scala/ocelot/desktop/inventory/item/WirelessNetworkCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/WirelessNetworkCardItem.scala index 414c46a..afec00e 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/WirelessNetworkCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/WirelessNetworkCardItem.scala @@ -2,6 +2,8 @@ package ocelot.desktop.inventory.item import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.{ItemFactory, ItemRecoverer} +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.WirelessNetworkCard import totoro.ocelot.brain.util.Tier import totoro.ocelot.brain.util.Tier.Tier @@ -10,7 +12,7 @@ abstract class WirelessNetworkCardItem(override val card: WirelessNetworkCard) e object WirelessNetworkCardItem { abstract class Factory extends ItemFactory { - override def name: String = s"Wireless Net. Card (${tier.get.label})" + override def name: Message = s"${m"component.modem.wireless"} (${m"${tier.get.label}"})" override def icon: IconSource = IconSource.Items.WirelessNetworkCard(tier.get) } diff --git a/src/main/scala/ocelot/desktop/inventory/traits/ComponentItem.scala b/src/main/scala/ocelot/desktop/inventory/traits/ComponentItem.scala index 53dab89..ceb1213 100644 --- a/src/main/scala/ocelot/desktop/inventory/traits/ComponentItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/traits/ComponentItem.scala @@ -5,6 +5,7 @@ import ocelot.desktop.inventory.Item import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.traits.{Entity, Environment} /** Implemented by [[Item]]s that wrap a component. */ @@ -18,11 +19,11 @@ trait ComponentItem extends EntityItem { super.fillTooltip(tooltip) if (showAddress && entity.node.address != null) { - tooltip.addLine(s"Address: ${entity.node.address}") + tooltip.addLine(s"${m"tooltip.address"}: ${entity.node.address}") } } - private val copyAddressEntry = ContextMenuEntry("Copy address", IconSource.Copy) { + private val copyAddressEntry = ContextMenuEntry(m"label.menu.copyAddress", IconSource.Copy) { UiHandler.clipboard = entity.node.address } diff --git a/src/main/scala/ocelot/desktop/inventory/traits/CpuLikeItem.scala b/src/main/scala/ocelot/desktop/inventory/traits/CpuLikeItem.scala index ee98e5e..cc13bf6 100644 --- a/src/main/scala/ocelot/desktop/inventory/traits/CpuLikeItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/traits/CpuLikeItem.scala @@ -5,6 +5,7 @@ import ocelot.desktop.inventory.Item import ocelot.desktop.inventory.traits.CpuLikeItem.CpuArchitectureChangedNotification import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuIcon, ContextMenuSubmenu} import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.machine.MachineAPI import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU} @@ -13,10 +14,10 @@ trait CpuLikeItem extends ComponentItem { override def entity: Entity with GenericCPU override def fillRmbMenu(menu: ContextMenu): Unit = { - menu.addEntry(new ContextMenuSubmenu("Set architecture", Some(ContextMenuIcon(IconSource.Microchip))) { + menu.addEntry(new ContextMenuSubmenu(m"label.menu.setArchitecture", Some(ContextMenuIcon(IconSource.Microchip))) { for (arch <- entity.allArchitectures) { val name = MachineAPI.getArchitectureName(arch) + - (if (arch == entity.architecture) " (current)" else "") + (if (arch == entity.architecture) s" (${m"label.menu.currentArchitecture"})" else "") val entry = ContextMenuEntry(name) { entity.setArchitecture(arch) notifySlot(CpuArchitectureChangedNotification) @@ -33,7 +34,7 @@ trait CpuLikeItem extends ComponentItem { override def fillTooltip(tooltip: ItemTooltip): Unit = { super.fillTooltip(tooltip) if (entity != null) { - tooltip.addLine(s"Components: ${entity.supportedComponents}") + tooltip.addLine(s"${m"tooltip.components"}: ${entity.supportedComponents}") } } } diff --git a/src/main/scala/ocelot/desktop/inventory/traits/DiskItem.scala b/src/main/scala/ocelot/desktop/inventory/traits/DiskItem.scala index d006806..a8071a6 100644 --- a/src/main/scala/ocelot/desktop/inventory/traits/DiskItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/traits/DiskItem.scala @@ -7,6 +7,8 @@ import ocelot.desktop.ui.widget.DiskEditWindow import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.tooltip.ItemTooltip import ocelot.desktop.ui.widget.window.{Window, Windowed} +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.traits.{Disk, DiskManaged, DiskRealPathAware, DiskUnmanaged, Entity} import totoro.ocelot.brain.util.DyeColor @@ -17,7 +19,7 @@ import scala.util.Try trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] { override def entity: Entity with Disk - def diskKind: String + def diskKind: Message def color: Option[DyeColor] = None @@ -68,26 +70,26 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] { } protected def addDiskLabelTooltip(tooltip: ItemTooltip, label: String): Unit = { - tooltip.addLine(s"Label: $label") + tooltip.addLine(s"${m"tooltip.disk.label"}: $label") } protected def addSourcePathTooltip(tooltip: ItemTooltip, disk: DiskManaged): Unit = { for (customRealPath <- disk.customRealPath) { - tooltip.addLine(s"Source path: $customRealPath") + tooltip.addLine(s"${m"tooltip.disk.sourcePath"}: $customRealPath") } } protected def addManagedTooltip(tooltip: ItemTooltip, disk: Disk): Unit = { tooltip.addLine(disk match { - case _: DiskManaged => "Mode: managed" - case _: DiskUnmanaged => "Mode: unmanaged" + case _: DiskManaged => m"tooltip.disk.managedMode" + case _: DiskUnmanaged => m"tooltip.disk.unmanagedMode" }) } override def fillTooltip(tooltip: ItemTooltip): Unit = { super.fillTooltip(tooltip) if (entity != null) { - tooltip.addLine(s"Capacity: ${entity.capacity / 1024} kB") + tooltip.addLine(s"${m"tooltip.disk.capacity"}: ${entity.capacity / 1024} kB") } } } @@ -96,7 +98,7 @@ object DiskItem { def addRealPathContextMenuEntries(menu: ContextMenu, diskRealPathAware: DiskRealPathAware, realPathSetter: (() => Unit) => Unit): Unit = { menu.addEntry(ContextMenuEntry( - if (diskRealPathAware.customRealPath.isDefined) "Change directory" else "Set directory", + if (diskRealPathAware.customRealPath.isDefined) m"label.menu.changeDirectory" else m"label.menu.setDirectory", IconSource.Folder, ) { OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir => @@ -112,7 +114,7 @@ object DiskItem { }) if (diskRealPathAware.customRealPath.isDefined) { - menu.addEntry(ContextMenuEntry("Reset directory", IconSource.FolderSlash) { + menu.addEntry(ContextMenuEntry(m"label.menu.resetDirectory", IconSource.FolderSlash) { realPathSetter(() => { // trigger component_removed / component_added signals diskRealPathAware.customRealPath = None @@ -122,7 +124,7 @@ object DiskItem { } def addEditDiskContextMenuEntries[T <: Window](menu: ContextMenu, windowed: Windowed[T]): Unit = { - menu.addEntry(ContextMenuEntry("Edit disk", IconSource.Edit) { + menu.addEntry(ContextMenuEntry(m"label.menu.editDisk", IconSource.Edit) { windowed.window.open() }) } diff --git a/src/main/scala/ocelot/desktop/inventory/traits/GpuLikeItem.scala b/src/main/scala/ocelot/desktop/inventory/traits/GpuLikeItem.scala index dc36a72..f53323b 100644 --- a/src/main/scala/ocelot/desktop/inventory/traits/GpuLikeItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/traits/GpuLikeItem.scala @@ -1,6 +1,7 @@ package ocelot.desktop.inventory.traits import ocelot.desktop.ui.widget.tooltip.ItemTooltip +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.Settings import totoro.ocelot.brain.entity.traits.{Entity, GenericGPU} import totoro.ocelot.brain.util.ColorDepth @@ -12,14 +13,14 @@ trait GpuLikeItem extends ComponentItem { super.fillTooltip(tooltip) if (entity != null) { val resolution = Settings.screenResolutionsByTier(entity.tier.id) - tooltip.addLine(s"Max resolution: ${resolution._1}×${resolution._2}") + tooltip.addLine(s"${m"tooltip.gpu.maxResolution"}: ${resolution._1}×${resolution._2}") val depth = Settings.screenDepthsByTier(entity.tier.id) match { - case ColorDepth.OneBit => "1 bit" - case ColorDepth.FourBit => "4 bit" - case ColorDepth.EightBit => "8 bit" + case ColorDepth.OneBit => "1" + case ColorDepth.FourBit => "4" + case ColorDepth.EightBit => "8" } - tooltip.addLine(s"Max color depth: $depth") - tooltip.addLine(s"VRAM: ${entity.totalVRAM.toInt}") + tooltip.addLine(s"${m"tooltip.gpu.maxColorDepth"}: $depth ${m"tooltip.bit"}") + tooltip.addLine(s"${m"tooltip.gpu.vram"}: ${entity.totalVRAM.toInt}") } } } diff --git a/src/main/scala/ocelot/desktop/node/EntityNode.scala b/src/main/scala/ocelot/desktop/node/EntityNode.scala index d7a7768..2526796 100644 --- a/src/main/scala/ocelot/desktop/node/EntityNode.scala +++ b/src/main/scala/ocelot/desktop/node/EntityNode.scala @@ -5,6 +5,7 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.event.ClickEvent import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.traits.{Entity, Environment, SidedEnvironment} import totoro.ocelot.brain.nbt.NBTTagCompound import totoro.ocelot.brain.network @@ -35,7 +36,7 @@ abstract class EntityNode(val entity: Entity with Environment) extends Node { override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = { if (exposeAddress && entity.node != null && entity.node.address != null) { menu.addEntry( - ContextMenuEntry("Copy address", IconSource.Copy) { + ContextMenuEntry(m"label.menu.copyAddress", IconSource.Copy) { UiHandler.clipboard = entity.node.address } ) diff --git a/src/main/scala/ocelot/desktop/node/LabeledNode.scala b/src/main/scala/ocelot/desktop/node/LabeledNode.scala index a3b7644..ed2ba4d 100644 --- a/src/main/scala/ocelot/desktop/node/LabeledNode.scala +++ b/src/main/scala/ocelot/desktop/node/LabeledNode.scala @@ -6,6 +6,7 @@ import ocelot.desktop.ui.event.ClickEvent import ocelot.desktop.ui.widget.InputDialog import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.util.DrawUtils +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.nbt.NBTTagCompound trait LabeledNode extends Node { @@ -26,9 +27,9 @@ trait LabeledNode extends Node { } override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = { - menu.addEntry(ContextMenuEntry("Set label", IconSource.Label) { + menu.addEntry(ContextMenuEntry(m"label.menu.setLabel", IconSource.Label) { new InputDialog( - "Set label", + m"label.menu.setLabel", text => { _label = Option.unless(text.isEmpty)(text) }, diff --git a/src/main/scala/ocelot/desktop/node/Node.scala b/src/main/scala/ocelot/desktop/node/Node.scala index 7a83cf7..4258e72 100644 --- a/src/main/scala/ocelot/desktop/node/Node.scala +++ b/src/main/scala/ocelot/desktop/node/Node.scala @@ -10,6 +10,7 @@ import ocelot.desktop.ui.event.sources.KeyEvents import ocelot.desktop.ui.event.{ClickEvent, DragEvent, HoverEvent, MouseEvent} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.{Widget, WorkspaceView} +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.Persistable import ocelot.desktop.util.animation.ColorAnimation import totoro.ocelot.brain.nbt.NBTTagCompound @@ -89,12 +90,12 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = { if (ports.nonEmpty) { - menu.addEntry(ContextMenuEntry("Disconnect", IconSource.LinkSlash, SoundSource.InterfaceClickLow) { + menu.addEntry(ContextMenuEntry(m"label.menu.disconnect", IconSource.LinkSlash, SoundSource.InterfaceClickLow) { disconnectFromAll() }) } - menu.addEntry(ContextMenuEntry("Remove", IconSource.Delete, SoundSource.InterfaceClickLow) { + menu.addEntry(ContextMenuEntry(m"label.menu.remove", IconSource.Delete, SoundSource.InterfaceClickLow) { destroy() }) } @@ -103,11 +104,11 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers super.update() if (isHovered || isMoving) { - root.get.statusBar.addMouseEntry("icons/RMB", "Menu") - root.get.statusBar.addMouseEntry("icons/DragLMB", "Move node") + root.get.statusBar.addMouseEntry("icons/RMB", m"label.bar.menu") + root.get.statusBar.addMouseEntry("icons/DragLMB", m"label.bar.moveNode") if (ports.nonEmpty) { - root.get.statusBar.addMouseEntry("icons/DragRMB", "Connect/Disconnect") + root.get.statusBar.addMouseEntry("icons/DragRMB", m"label.bar.connectDisconnect") } } } diff --git a/src/main/scala/ocelot/desktop/node/NodeRegistry.scala b/src/main/scala/ocelot/desktop/node/NodeRegistry.scala index d2058c6..fa74a79 100644 --- a/src/main/scala/ocelot/desktop/node/NodeRegistry.scala +++ b/src/main/scala/ocelot/desktop/node/NodeRegistry.scala @@ -3,10 +3,9 @@ package ocelot.desktop.node import ocelot.desktop.entity.{Camera, OcelotBlock, OpenFMRadio} import ocelot.desktop.graphics.IconSource import ocelot.desktop.node.nodes._ -import totoro.ocelot.brain.entity.{ - Cable, Case, ColorfulLamp, FloppyDiskDrive, HologramProjector, IronNoteBlock, Microcontroller, NoteBlock, Rack, Raid, - Relay, Screen, TapeDrive, -} +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage +import totoro.ocelot.brain.entity.{Cable, Case, ColorfulLamp, FloppyDiskDrive, HologramProjector, IronNoteBlock, Microcontroller, NoteBlock, Rack, Raid, Relay, Screen, TapeDrive} import totoro.ocelot.brain.util.Tier import scala.collection.mutable @@ -16,7 +15,7 @@ object NodeRegistry { private var group: NodeTypeGroup = _ - private def nextGroup(name: String): Unit = { + private def nextGroup(name: Message): Unit = { group = new NodeTypeGroup(name) groups += group } @@ -27,89 +26,89 @@ object NodeRegistry { // ------------------------------ Original nodes ------------------------------ - nextGroup("Original") + nextGroup(m"selector.group.original") for (tier <- Tier.One to Tier.Creative) { - addType(NodeType(s"Computer Case (${tier.label})", IconSource.Nodes.Computer.Default, tier) { + addType(NodeType(m"component.computer", IconSource.Nodes.Computer.Default, tier) { new ComputerNode(new Case(tier)) }) } - addType(NodeType("Rack", IconSource.Nodes.Rack.Default, None) { + addType(NodeType(m"component.rack", IconSource.Nodes.Rack.Default, None) { new RackNode(new Rack) }) for (tier <- Tier.One to Tier.Three) { - addType(NodeType(s"Screen (${tier.label})", IconSource.Nodes.Screen.Standalone, tier) { + addType(NodeType(m"component.screen", IconSource.Nodes.Screen.Standalone, tier) { val node = new ScreenNode(new Screen(tier)) node.attachKeyboard() node }) } - addType(NodeType("Disk Drive", IconSource.Nodes.DiskDrive.Default, None) { + addType(NodeType(m"component.diskDrive", IconSource.Nodes.DiskDrive.Default, None) { new DiskDriveNode(new FloppyDiskDrive()) }) - addType(NodeType("Raid", IconSource.Nodes.Raid.Default, None) { + addType(NodeType(m"component.raid", IconSource.Nodes.Raid.Default, None) { new RaidNode(new Raid) }) for (tier <- Tier.One to Tier.Two) { - addType(NodeType(s"Hologram Projector (${tier.label})", IconSource.Nodes.HologramProjector(tier), None) { + addType(NodeType(m"component.hologramProjector", IconSource.Nodes.HologramProjector(tier), tier) { new HologramProjectorNode(new HologramProjector(tier)) }) } - addType(NodeType("Note Block", IconSource.Nodes.NoteBlock, None) { + addType(NodeType(m"component.noteBlock", IconSource.Nodes.NoteBlock, None) { new NoteBlockNode(new NoteBlock) }) - addType(NodeType("Microcontroller", IconSource.Nodes.Microcontroller.Default, None) { + addType(NodeType(m"component.microcontroller", IconSource.Nodes.Microcontroller.Default, None) { new MicrocontrollerNode(new Microcontroller(Tier.Two)) }) - addType(NodeType("Relay", IconSource.Nodes.Relay, None) { + addType(NodeType(m"component.relay", IconSource.Nodes.Relay, None) { new RelayNode(new Relay) }) - addType(NodeType("Chest", IconSource.Nodes.Chest, None) { + addType(NodeType(m"component.chest", IconSource.Nodes.Chest, None) { new ChestNode }) - addType(NodeType("Cable", IconSource.Nodes.Cable, None) { + addType(NodeType(m"component.cable", IconSource.Nodes.Cable, None) { new CableNode(new Cable) }) // ------------------------------ Addon nodes ------------------------------ - nextGroup("Addons") + nextGroup(m"selector.group.addons") - addType(NodeType("Iron Note Block", IconSource.Nodes.IronNoteBlock, None) { + addType(NodeType(m"component.ironNoteBlock", IconSource.Nodes.IronNoteBlock, None) { new IronNoteBlockNode(new IronNoteBlock) }) - addType(NodeType("Camera", IconSource.Nodes.Camera, None) { + addType(NodeType(m"component.camera", IconSource.Nodes.Camera, None) { new CameraNode(new Camera) }) - addType(NodeType("Colorful Lamp", IconSource.Nodes.Lamp, None) { + addType(NodeType(m"component.colorfulLamp", IconSource.Nodes.Lamp, None) { new ColorfulLampNode(new ColorfulLamp) }) - addType(NodeType("OpenFM Radio", IconSource.Nodes.OpenFMRadio, None) { + addType(NodeType(m"component.openFmRadio", IconSource.Nodes.OpenFMRadio, None) { new OpenFMRadioNode(new OpenFMRadio) }) - addType(NodeType("Tape Drive", IconSource.Nodes.TapeDrive, None) { + addType(NodeType(m"component.tapeDrive", IconSource.Nodes.TapeDrive, None) { new TapeDriveNode(new TapeDrive) }) // ------------------------------ Custom nodes ------------------------------ - nextGroup("Custom") + nextGroup(m"selector.group.custom") - addType(NodeType("Ocelot Block", IconSource.Nodes.OcelotBlock.Default, None) { + addType(NodeType(m"component.ocelot.block", IconSource.Nodes.OcelotBlock.Default, None) { new OcelotBlockNode(new OcelotBlock) }) } diff --git a/src/main/scala/ocelot/desktop/node/NodeType.scala b/src/main/scala/ocelot/desktop/node/NodeType.scala index 8399cc2..408b738 100644 --- a/src/main/scala/ocelot/desktop/node/NodeType.scala +++ b/src/main/scala/ocelot/desktop/node/NodeType.scala @@ -1,19 +1,24 @@ package ocelot.desktop.node import ocelot.desktop.graphics.IconSource +import ocelot.desktop.util.Message import totoro.ocelot.brain.util.Tier.Tier -class NodeType(val name: String, val icon: IconSource, val tier: Option[Tier], factory: => Node) +class NodeType(val name: Message, val icon: IconSource, val tier: Option[Tier], factory: => Node) extends Ordered[NodeType] { def make(): Node = factory - override def compare(that: NodeType): Int = this.name.compare(that.name) + override def compare(that: NodeType): Int = { + val compareNames = this.name.value.compare(that.name.value) + if (compareNames == 0) this.tier.map(_.num).getOrElse(0).compare(that.tier.map(_.num).getOrElse(0)) + else compareNames + } } object NodeType { - def apply(name: String, icon: IconSource, tier: Tier)(factory: => Node): NodeType = + def apply(name: Message, icon: IconSource, tier: Tier)(factory: => Node): NodeType = new NodeType(name, icon, Some(tier), factory) - def apply(name: String, icon: IconSource, tier: Option[Tier])(factory: => Node): NodeType = + def apply(name: Message, icon: IconSource, tier: Option[Tier])(factory: => Node): NodeType = new NodeType(name, icon, tier, factory) } diff --git a/src/main/scala/ocelot/desktop/node/NodeTypeGroup.scala b/src/main/scala/ocelot/desktop/node/NodeTypeGroup.scala index 5f57dca..244d1ad 100644 --- a/src/main/scala/ocelot/desktop/node/NodeTypeGroup.scala +++ b/src/main/scala/ocelot/desktop/node/NodeTypeGroup.scala @@ -1,7 +1,9 @@ package ocelot.desktop.node +import ocelot.desktop.util.Message + import scala.collection.mutable -class NodeTypeGroup(val name: String) { +class NodeTypeGroup(val name: Message) { val types: mutable.ArrayBuffer[NodeType] = mutable.ArrayBuffer[NodeType]() } diff --git a/src/main/scala/ocelot/desktop/node/NodeTypeWidget.scala b/src/main/scala/ocelot/desktop/node/NodeTypeWidget.scala index 641a675..c25cade 100644 --- a/src/main/scala/ocelot/desktop/node/NodeTypeWidget.scala +++ b/src/main/scala/ocelot/desktop/node/NodeTypeWidget.scala @@ -8,6 +8,7 @@ import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler} import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent} import ocelot.desktop.ui.widget.Widget import ocelot.desktop.ui.widget.tooltip.LabelTooltip +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.{Spritesheet, TierColor} class NodeTypeWidget(val nodeType: NodeType) extends Widget with MouseHandler with HoverHandler { @@ -27,7 +28,7 @@ class NodeTypeWidget(val nodeType: NodeType) extends Widget with MouseHandler wi else onHoverLeave() } - private val labelTooltip = new LabelTooltip(nodeType.name) + private val labelTooltip = new LabelTooltip(nodeType.name + nodeType.tier.map(tier => s" (${m"${tier.label}"})").getOrElse("")) def onHoverEnter(): Unit = root.get.tooltipPool.addTooltip(labelTooltip) @@ -44,7 +45,7 @@ class NodeTypeWidget(val nodeType: NodeType) extends Widget with MouseHandler wi position.y + Size / 2 - size.height / 2, size.width, size.height, - nodeType.tier.map(TierColor.get).getOrElse(Color.White), + if (nodeType.icon.tierTint) nodeType.tier.map(TierColor.get).getOrElse(Color.White) else Color.White, nodeType.icon.animation, ) } @@ -52,7 +53,7 @@ class NodeTypeWidget(val nodeType: NodeType) extends Widget with MouseHandler wi override def update(): Unit = { super.update() if (isHovered) { - root.get.statusBar.addMouseEntry("icons/LMB", "Add node") + root.get.statusBar.addMouseEntry("icons/LMB", m"label.bar.addNode") } } } diff --git a/src/main/scala/ocelot/desktop/node/ShiftClickNode.scala b/src/main/scala/ocelot/desktop/node/ShiftClickNode.scala index 3293c64..a082344 100644 --- a/src/main/scala/ocelot/desktop/node/ShiftClickNode.scala +++ b/src/main/scala/ocelot/desktop/node/ShiftClickNode.scala @@ -2,10 +2,11 @@ package ocelot.desktop.node import ocelot.desktop.ui.event.sources.KeyEvents import ocelot.desktop.ui.event.{ClickEvent, MouseEvent} +import ocelot.desktop.util.Message trait ShiftClickNode extends Node { protected def onShiftClick(event: ClickEvent): Unit - protected def hoveredShiftStatusBarText: String + protected def hoveredShiftStatusBarText: Message override def onClick(event: ClickEvent): Unit = { event match { diff --git a/src/main/scala/ocelot/desktop/node/WindowedNode.scala b/src/main/scala/ocelot/desktop/node/WindowedNode.scala index a09e05c..105d08e 100644 --- a/src/main/scala/ocelot/desktop/node/WindowedNode.scala +++ b/src/main/scala/ocelot/desktop/node/WindowedNode.scala @@ -3,7 +3,7 @@ package ocelot.desktop.node import ocelot.desktop.ui.event.sources.KeyEvents import ocelot.desktop.ui.event.{ClickEvent, MouseEvent} import ocelot.desktop.ui.widget.window.{Window, Windowed} -import org.lwjgl.input.Keyboard +import ocelot.desktop.util.Message.LocalizedMessage trait WindowedNode[T <: Window] extends Node with Windowed[T] { override def dispose(): Unit = { @@ -14,7 +14,7 @@ trait WindowedNode[T <: Window] extends Node with Windowed[T] { override def update(): Unit = { if (isHovered || isMoving) - root.get.statusBar.addMouseEntry("icons/LMB", if (windowCreated && window.isOpen) "Close" else "Open") + root.get.statusBar.addMouseEntry("icons/LMB", if (windowCreated && window.isOpen) m"label.bar.close" else m"label.bar.open") super.update() } diff --git a/src/main/scala/ocelot/desktop/node/nodes/ComputerNode.scala b/src/main/scala/ocelot/desktop/node/nodes/ComputerNode.scala index a536982..9037781 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/ComputerNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/ComputerNode.scala @@ -8,7 +8,8 @@ import ocelot.desktop.ui.event.ClickEvent import ocelot.desktop.ui.widget.contextmenu.ContextMenu import ocelot.desktop.ui.widget.slot._ import ocelot.desktop.util.ComputerType.ComputerType -import ocelot.desktop.util.{AudibleComputerAware, ComputerType, DrawUtils, TierColor} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{AudibleComputerAware, ComputerType, DrawUtils, Message, TierColor} import ocelot.desktop.windows.ComputerWindow import totoro.ocelot.brain.entity.Case import totoro.ocelot.brain.entity.traits.Inventory @@ -113,5 +114,5 @@ class ComputerNode(val computerCase: Case) override protected def onShiftClick(event: ClickEvent): Unit = toggleIsTurnedOn() - override protected def hoveredShiftStatusBarText: String = if (computer.machine.isRunning) "Turn off" else "Turn on" + override protected def hoveredShiftStatusBarText: Message = if (computer.machine.isRunning) m"label.menu.turnOff" else m"label.menu.turnOn" } diff --git a/src/main/scala/ocelot/desktop/node/nodes/DiskDriveNode.scala b/src/main/scala/ocelot/desktop/node/nodes/DiskDriveNode.scala index e848510..e3b7190 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/DiskDriveNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/DiskDriveNode.scala @@ -8,7 +8,8 @@ import ocelot.desktop.node.{EntityNode, LabeledEntityNode, ShiftClickNode, Windo import ocelot.desktop.ui.event.ClickEvent import ocelot.desktop.ui.event.handlers.DiskActivityHandler import ocelot.desktop.ui.widget.contextmenu.ContextMenu -import ocelot.desktop.util.DiskDriveAware +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{DiskDriveAware, Message} import ocelot.desktop.windows.DiskDriveWindow import totoro.ocelot.brain.entity.FloppyDiskDrive @@ -63,5 +64,5 @@ class DiskDriveNode(entity: FloppyDiskDrive) eject() } - override protected def hoveredShiftStatusBarText: String = "Eject floppy" + override protected def hoveredShiftStatusBarText: Message = m"label.menu.ejectFloppy" } diff --git a/src/main/scala/ocelot/desktop/node/nodes/IronNoteBlockNode.scala b/src/main/scala/ocelot/desktop/node/nodes/IronNoteBlockNode.scala index f1d6a43..89024f9 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/IronNoteBlockNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/IronNoteBlockNode.scala @@ -3,6 +3,8 @@ package ocelot.desktop.node.nodes import ocelot.desktop.graphics.IconSource import ocelot.desktop.node.nodes.IronNoteBlockNode.{Instruments, NumberOfPitches} import ocelot.desktop.ui.event.{ClickEvent, MouseEvent} +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.IronNoteBlock import totoro.ocelot.brain.event.{EventBus, NoteBlockTriggerEvent} @@ -30,17 +32,17 @@ class IronNoteBlockNode(val ironNoteBlock: IronNoteBlock) extends NoteBlockNodeB object IronNoteBlockNode { val NumberOfPitches: Int = 24 - val Instruments: List[(String, String)] = List( - ("harp", "Harp"), - ("basedrum", "Bass Drum (Stone)"), - ("snare", "Snare Drum (Sand)"), - ("hat", "Hi-hat (Glass)"), - ("bass", "Bass (Wood)"), - ("flute", "Flute (Clay)"), - ("bell", "Glockenspiel (Gold)"), - ("guitar", "Guitar (Wool)"), - ("chime", "Chimes (Packed Ice)"), - ("xylophone", "Xylophone (Bone)"), - ("pling", "Electric Piano (Glowstone)"), + val Instruments: List[(String, Message)] = List( + ("harp", m"component.noteBlock.harp"), + ("basedrum", m"component.noteBlock.basedrum"), + ("snare", m"component.noteBlock.snare"), + ("hat", m"component.noteBlock.hat"), + ("bass", m"component.noteBlock.bass"), + ("flute", m"component.noteBlock.flute"), + ("bell", m"component.noteBlock.bell"), + ("guitar", m"component.noteBlock.guitar"), + ("chime", m"component.noteBlock.chime"), + ("xylophone", m"component.noteBlock.xylophone"), + ("pling", m"component.noteBlock.pling"), ) } diff --git a/src/main/scala/ocelot/desktop/node/nodes/MicrocontrollerNode.scala b/src/main/scala/ocelot/desktop/node/nodes/MicrocontrollerNode.scala index ff80185..eb72346 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/MicrocontrollerNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/MicrocontrollerNode.scala @@ -8,7 +8,8 @@ import ocelot.desktop.ui.event.ClickEvent import ocelot.desktop.ui.widget.contextmenu.ContextMenu import ocelot.desktop.ui.widget.slot._ import ocelot.desktop.util.ComputerType.ComputerType -import ocelot.desktop.util.{ComputerAware, ComputerType, DefaultSlotItemsFillable, DrawUtils} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{ComputerAware, ComputerType, DefaultSlotItemsFillable, DrawUtils, Message} import ocelot.desktop.windows.ComputerWindow import totoro.ocelot.brain.entity.Microcontroller import totoro.ocelot.brain.entity.traits.Inventory @@ -145,5 +146,5 @@ class MicrocontrollerNode(val microcontroller: Microcontroller) override protected def onShiftClick(event: ClickEvent): Unit = toggleIsTurnedOn() - override protected def hoveredShiftStatusBarText: String = if (computer.machine.isRunning) "Turn off" else "Turn on" + override protected def hoveredShiftStatusBarText: Message = if (computer.machine.isRunning) m"label.menu.turnOff" else m"label.menu.turnOn" } diff --git a/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNode.scala b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNode.scala index 62291fd..7737572 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNode.scala @@ -3,6 +3,7 @@ package ocelot.desktop.node.nodes import ocelot.desktop.graphics.IconSource import ocelot.desktop.ui.event.{ClickEvent, MouseEvent} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuIcon, ContextMenuSubmenu} +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.NoteBlock import totoro.ocelot.brain.event.{EventBus, NoteBlockTriggerEvent} @@ -10,14 +11,14 @@ class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBloc override def iconSource: IconSource = IconSource.Nodes.NoteBlock override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = { - menu.addEntry(new ContextMenuSubmenu("Instrument", Some(ContextMenuIcon(IconSource.Guitar))) { + menu.addEntry(new ContextMenuSubmenu(m"label.menu.instrument", Some(ContextMenuIcon(IconSource.Guitar))) { { - val maxLen = NoteBlockNode.Instruments.map(_._2.length).max + val maxLen = NoteBlockNode.Instruments.map(_._2.toString.length).max for ((instrument, name) <- NoteBlockNode.Instruments) { val dot = if (noteBlock.instrument == instrument) '•' else ' ' - addEntry(ContextMenuEntry(name.padTo(maxLen, ' ') + dot) { + addEntry(ContextMenuEntry(name.toString.padTo(maxLen, ' ') + dot) { noteBlock.instrument = instrument }) } @@ -43,21 +44,21 @@ class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBloc object NoteBlockNode { private val Instruments = List( - ("bass", "Bass (Wood)"), - ("snare", "Snare Drum (Sand)"), - ("hat", "Hi-hat (Glass)"), - ("basedrum", "Bass Drum (Stone)"), - ("bell", "Glockenspiel (Gold)"), - ("flute", "Flute (Clay)"), - ("chime", "Chimes (Packed Ice)"), - ("guitar", "Guitar (Wool)"), - ("xylophone", "Xylophone (Bone)"), - ("iron_xylophone", "Vibraphone (Iron)"), - ("cow_bell", "Cow Bell (Soul Sand)"), - ("didgeridoo", "Didgeridoo (Pumpkin)"), - ("bit", "Square Wave (Emerald)"), - ("banjo", "Banjo (Hay Bale)"), - ("pling", "Electric Piano (Glowstone)"), - ("harp", "Harp"), + ("bass", m"component.noteBlock.bass"), + ("snare", m"component.noteBlock.snare"), + ("hat", m"component.noteBlock.hat"), + ("basedrum", m"component.noteBlock.basedrum"), + ("bell", m"component.noteBlock.bell"), + ("flute", m"component.noteBlock.flute"), + ("chime", m"component.noteBlock.chime"), + ("guitar", m"component.noteBlock.guitar"), + ("xylophone", m"component.noteBlock.xylophone"), + ("iron_xylophone", m"component.noteBlock.ironXylophone"), + ("cow_bell", m"component.noteBlock.cowBell"), + ("didgeridoo", m"component.noteBlock.didgeridoo"), + ("bit", m"component.noteBlock.bit"), + ("banjo", m"component.noteBlock.banjo"), + ("pling", m"component.noteBlock.pling"), + ("harp", m"component.noteBlock.harp"), ) } diff --git a/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala index ea4f2ff..bacd5c8 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala @@ -7,6 +7,7 @@ import ocelot.desktop.graphics.Graphics import ocelot.desktop.node.{EntityNode, LabeledEntityNode} import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.event.BrainEvent +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.entity.traits.{Entity, Environment} import totoro.ocelot.brain.event.NoteBlockTriggerEvent @@ -47,6 +48,6 @@ abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Entity super.update() if (isHovered || isMoving) - root.get.statusBar.addMouseEntry("icons/LMB", "Play sample") + root.get.statusBar.addMouseEntry("icons/LMB", m"label.bar.playSample") } } diff --git a/src/main/scala/ocelot/desktop/node/nodes/OcelotBlockNode.scala b/src/main/scala/ocelot/desktop/node/nodes/OcelotBlockNode.scala index d0a2c5a..d5f4540 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/OcelotBlockNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/OcelotBlockNode.scala @@ -10,7 +10,8 @@ import ocelot.desktop.node.nodes.OcelotBlockNode.ActivityFadeOutMs import ocelot.desktop.node.{EntityNode, LabeledEntityNode, OcelotLogParticleNode, WindowedNode} import ocelot.desktop.ui.widget.LogWidget import ocelot.desktop.ui.widget.LogWidget.LogEntry -import ocelot.desktop.util.OcelotInterfaceLogStorage +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, OcelotInterfaceLogStorage} import ocelot.desktop.windows.OcelotInterfaceWindow class OcelotBlockNode(val ocelot: OcelotBlock) @@ -20,7 +21,7 @@ class OcelotBlockNode(val ocelot: OcelotBlock) with OcelotInterfaceLogStorage with WindowedNode[OcelotInterfaceWindow] { - override def name: String = "Ocelot Block" + override def name: Message = m"component.ocelot.block" override def iconSource: IconSource = IconSource.Nodes.OcelotBlock.Default diff --git a/src/main/scala/ocelot/desktop/node/nodes/RackNode.scala b/src/main/scala/ocelot/desktop/node/nodes/RackNode.scala index 2081665..8047f45 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/RackNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/RackNode.scala @@ -9,7 +9,8 @@ import ocelot.desktop.node.Node.{HighlightThickness, NoHighlightSize, Size, Texe import ocelot.desktop.node.{ComputerAwareNode, NodePort, WindowedNode} import ocelot.desktop.ui.event.{BrainEvent, ClickEvent} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} -import ocelot.desktop.util.DrawUtils +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{DrawUtils, Message} import ocelot.desktop.windows.RackWindow import totoro.ocelot.brain.entity.traits.{ComponentInventory, Environment, Inventory, RackMountable} import totoro.ocelot.brain.entity.{Rack, Server} @@ -162,7 +163,7 @@ class RackNode(val rack: Rack) extends ComputerAwareNode(rack) with WindowedNode } } - override protected def hoveredShiftStatusBarText: String = "Turn server on/off" + override protected def hoveredShiftStatusBarText: Message = m"label.bar.turnServerOnOff" // ---------------------------- Event handlers ---------------------------- @@ -183,7 +184,7 @@ object RackNode { def addContextMenuEntriesOfMountable(menu: ContextMenu, item: Option[RackMountableItem]): Unit = { item match { case Some(serverItem: ServerItem) => - menu.addEntry(ContextMenuEntry("Set up", IconSource.Window) { + menu.addEntry(ContextMenuEntry(m"label.menu.setup", IconSource.Window) { serverItem.window.open() }) @@ -203,9 +204,9 @@ object RackNode { menu.addEntry(ContextMenuEntry( if (diskDriveMountableItem.isFloppyItemPresent) - "Change floppy" + m"label.menu.changeFloppy" else - "Set floppy", + m"label.menu.setFloppy", IconSource.Save, ) { diskDriveMountableItem.window.open() diff --git a/src/main/scala/ocelot/desktop/node/nodes/ScreenNode.scala b/src/main/scala/ocelot/desktop/node/nodes/ScreenNode.scala index 5214337..cd8a823 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/ScreenNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/ScreenNode.scala @@ -11,6 +11,7 @@ import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.event.ClickEvent import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.{ScreenAspectRatioDialog, TickUpdatable} +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.TierColor import ocelot.desktop.windows.ScreenWindow import ocelot.desktop.{OcelotDesktop, Settings} @@ -98,25 +99,25 @@ class ScreenNode(val screen: Screen) override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = { // no synchronization here: the methods to turn the screen on/off are indirect. if (screen.getPowerState) { - menu.addEntry(ContextMenuEntry("Turn off", IconSource.Power) { + menu.addEntry(ContextMenuEntry(m"label.menu.turnOff", IconSource.Power) { screen.setPowerState(false) }) } else { - menu.addEntry(ContextMenuEntry("Turn on", IconSource.Power) { + menu.addEntry(ContextMenuEntry(m"label.menu.turnOn", IconSource.Power) { screen.setPowerState(true) }) } - menu.addEntry(ContextMenuEntry("Set aspect ratio", IconSource.AspectRatio) { + menu.addEntry(ContextMenuEntry(m"label.menu.setAspectRatio", IconSource.AspectRatio) { new ScreenAspectRatioDialog(this).show() }) if (keyboard.isDefined) { - menu.addEntry(ContextMenuEntry("Remove keyboard", IconSource.KeyboardOff) { + menu.addEntry(ContextMenuEntry(m"label.menu.removeKeyboard", IconSource.KeyboardOff) { detachKeyboard() }) } else { - menu.addEntry(ContextMenuEntry("Add keyboard", IconSource.Keyboard) { + menu.addEntry(ContextMenuEntry(m"label.menu.addKeyboard", IconSource.Keyboard) { attachKeyboard() }) } diff --git a/src/main/scala/ocelot/desktop/ui/swing/SplashScreen.scala b/src/main/scala/ocelot/desktop/ui/swing/SplashScreen.scala index 58757b2..d786665 100644 --- a/src/main/scala/ocelot/desktop/ui/swing/SplashScreen.scala +++ b/src/main/scala/ocelot/desktop/ui/swing/SplashScreen.scala @@ -1,6 +1,7 @@ package ocelot.desktop.ui.swing import buildinfo.BuildInfo +import ocelot.desktop.util.Message import java.awt.Window.Type import java.awt.{Color, Dimension, Font, Insets, Rectangle} @@ -92,8 +93,8 @@ class SplashScreen extends JDialog { progressBar.setBounds(0, 440, (imageWidth * percent).toInt, 3) } - def setStatus(text: String, percent: Float): Unit = { - status.setText(text) + def setStatus(text: Message, percent: Float): Unit = { + status.setText(text.toString) setProgress(percent) } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/Button.scala b/src/main/scala/ocelot/desktop/ui/widget/Button.scala index 7e983ac..134a0ca 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Button.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Button.scala @@ -9,7 +9,7 @@ import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler} import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent} import ocelot.desktop.ui.widget.tooltip.Tooltip import ocelot.desktop.ui.widget.traits.HoverAnimation -import ocelot.desktop.util.DrawUtils +import ocelot.desktop.util.{DrawUtils, Message} class Button(tooltip: Option[Tooltip] = None) extends Widget with MouseHandler with HoverHandler with HoverAnimation { @@ -19,7 +19,7 @@ class Button(tooltip: Option[Tooltip] = None) extends Widget with MouseHandler w override protected val hoverAnimationColorDefault: Color = colorScheme("ButtonBackground") override protected val hoverAnimationColorActive: Color = colorScheme("ButtonBackgroundActive") - def text: String = "" + def text: Message = "" def onClick(): Unit = {} @@ -41,7 +41,7 @@ class Button(tooltip: Option[Tooltip] = None) extends Widget with MouseHandler w } } - override def minimumSize: Size2D = Size2D(24 + text.length * 8, 24) + override def minimumSize: Size2D = Size2D(24 + text.toString.length * 8, 24) override def maximumSize: Size2D = minimumSize @@ -62,8 +62,8 @@ class Button(tooltip: Option[Tooltip] = None) extends Widget with MouseHandler w g.background = Color.Transparent g.foreground = foreground - val textWidth = text.iterator.map(g.font.charWidth(_)).sum - g.text(position.x + ((width - textWidth) / 2).round, position.y + 4, text) + val textWidth = text.toString.iterator.map(g.font.charWidth(_)).sum + g.text(position.x + ((width - textWidth) / 2).round, position.y + 4, text.toString) } protected def clickSoundSource: ClickSoundSource = SoundSource.InterfaceClick diff --git a/src/main/scala/ocelot/desktop/ui/widget/ChangeSimulationSpeedDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/ChangeSimulationSpeedDialog.scala index bc4d1ad..e13fae8 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/ChangeSimulationSpeedDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/ChangeSimulationSpeedDialog.scala @@ -6,7 +6,8 @@ import ocelot.desktop.geometry.Padding2D import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.ChangeSimulationSpeedDialog.validateIntervalUs import ocelot.desktop.ui.widget.modal.ModalDialog -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} import scala.concurrent.duration.{Duration, DurationLong} @@ -25,7 +26,7 @@ class ChangeSimulationSpeedDialog extends ModalDialog { new Widget { override val layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= new PaddingBox(new Label("Change simulation speed"), Padding2D(bottom = 16)) + children :+= new PaddingBox(new Label(m"label.menu.changeSimulationSpeed"), Padding2D(bottom = 16)) private var inputTPS: TextInput = _ private var inputMSPT: TextInput = _ @@ -76,9 +77,9 @@ class ChangeSimulationSpeedDialog extends ModalDialog { override def onConfirm(): Unit = confirm() } - children :+= new Label("Milliseconds per tick:") + children :+= new Label(m"label.dialog.speed.msPerTick") children :+= new PaddingBox(inputMSPT, Padding2D(bottom = 8)) - children :+= new Label("Ticks per second:") + children :+= new Label(m"label.dialog.speed.ticksPerSecond") children :+= new PaddingBox(inputTPS, Padding2D(bottom = 8)) children :+= new Widget { @@ -86,7 +87,7 @@ class ChangeSimulationSpeedDialog extends ModalDialog { children :+= new PaddingBox( new Button { - override def text: String = "Cancel" + override def text: Message = m"button.cancel" override protected def clickSoundSource: ClickSoundSource = SoundSource.InterfaceClickLow override def onClick(): Unit = close() }, @@ -94,7 +95,7 @@ class ChangeSimulationSpeedDialog extends ModalDialog { ) children :+= new Button { - override def text: String = "Apply" + override def text: Message = m"button.apply" override def onClick(): Unit = confirm() override def enabled: Boolean = tickInterval.nonEmpty } diff --git a/src/main/scala/ocelot/desktop/ui/widget/Checkbox.scala b/src/main/scala/ocelot/desktop/ui/widget/Checkbox.scala index c0e9dad..45d7276 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Checkbox.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Checkbox.scala @@ -7,9 +7,9 @@ import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.event.handlers.MouseHandler import ocelot.desktop.ui.event.{ClickEvent, MouseEvent} import ocelot.desktop.ui.widget.traits.HoverAnimation -import ocelot.desktop.util.DrawUtils +import ocelot.desktop.util.{DrawUtils, Message} -class Checkbox(val label: String, val initialValue: Boolean = false, val isSmall: Boolean = false) +class Checkbox(val label: Message, val initialValue: Boolean = false, val isSmall: Boolean = false) extends Widget with MouseHandler with HoverAnimation { override protected val hoverAnimationColorDefault: Color = ColorScheme("CheckboxBackground") @@ -42,7 +42,7 @@ class Checkbox(val label: String, val initialValue: Boolean = false, val isSmall private var corrected: Boolean = false private var textWidth = label.length * 8 - private def wideWidth(g: Graphics): Int = label.map(g.font.charWidth(_)).sum + private def wideWidth(g: Graphics): Int = label.toString.map(g.font.charWidth(_)).sum override def minimumSize: Size2D = Size2D(textWidth, if (isSmall) 8 else 16) override def maximumSize: Size2D = minimumSize.copy(width = Float.PositiveInfinity) @@ -68,7 +68,7 @@ class Checkbox(val label: String, val initialValue: Boolean = false, val isSmall } g.foreground = ColorScheme("CheckboxLabel") - g.text(position.x + boxSize + 8, position.y, label) + g.text(position.x + boxSize + 8, position.y, label.toString) g.setNormalFont() } diff --git a/src/main/scala/ocelot/desktop/ui/widget/CloseConfirmationDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/CloseConfirmationDialog.scala index 2759ca4..be23c87 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/CloseConfirmationDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/CloseConfirmationDialog.scala @@ -4,10 +4,11 @@ import ocelot.desktop.audio.{ClickSoundSource, SoundSource} import ocelot.desktop.geometry.Padding2D import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.modal.ModalDialog -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} class CloseConfirmationDialog extends ModalDialog { - protected def prompt: String = "Save workspace before exiting?" + protected def prompt: Message = m"notification.saveBeforeExit" children :+= new PaddingBox( new Widget { @@ -15,7 +16,7 @@ class CloseConfirmationDialog extends ModalDialog { children :+= new PaddingBox( new Label { - override def text: String = prompt + override def text: Message = prompt }, Padding2D(bottom = 16), ) @@ -23,7 +24,7 @@ class CloseConfirmationDialog extends ModalDialog { children :+= new Widget { children :+= new PaddingBox( new Button { - override def text: String = "Cancel" + override def text: Message = m"button.cancel" override protected def clickSoundSource: ClickSoundSource = SoundSource.InterfaceClickLow override def onClick(): Unit = close() }, @@ -34,7 +35,7 @@ class CloseConfirmationDialog extends ModalDialog { children :+= new PaddingBox( new Button { - override def text: String = "No" + override def text: Message = m"button.no" override def onClick(): Unit = onNoSaveSelected() }, Padding2D(right = 8), @@ -42,7 +43,7 @@ class CloseConfirmationDialog extends ModalDialog { children :+= new PaddingBox( new Button { - override def text: String = "Yes" + override def text: Message = m"button.yes" override def onClick(): Unit = onSaveSelected() }, Padding2D(right = 8), diff --git a/src/main/scala/ocelot/desktop/ui/widget/ComponentUsageBar.scala b/src/main/scala/ocelot/desktop/ui/widget/ComponentUsageBar.scala index 9afbae2..9bdeea6 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/ComponentUsageBar.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/ComponentUsageBar.scala @@ -3,11 +3,12 @@ package ocelot.desktop.ui.widget import ocelot.desktop.ColorScheme import ocelot.desktop.color.Color import ocelot.desktop.graphics.Graphics +import ocelot.desktop.util.Message.LocalizedMessage class ComponentUsageBar extends Widget with TickUpdatable { var maxValue = 0 var currentValue = 0 - var title = "Component usage" + var title = m"label.window.computer.componentUsage" private def drawText(g: Graphics, x: Float, y: Float, text: String): Unit = { g.setSmallFont() @@ -32,7 +33,7 @@ class ComponentUsageBar extends Widget with TickUpdatable { // Text drawText(g, x - marginLeft / 2 - 4, position.y + 3, currentValue.toString) drawText(g, x - marginLeft / 2 - 4, position.y + 18, s"($maxValue)") - drawText(g, x + gridWidth / 2, position.y + 18, title) + drawText(g, x + gridWidth / 2, position.y + 18, title.toString) // Horizontal lines g.rect(x, position.y, gridWidth, cellThickness, ColorScheme("HistogramGrid")) diff --git a/src/main/scala/ocelot/desktop/ui/widget/ComputerErrorMessageLabel.scala b/src/main/scala/ocelot/desktop/ui/widget/ComputerErrorMessageLabel.scala index afa21ca..233f3bc 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/ComputerErrorMessageLabel.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/ComputerErrorMessageLabel.scala @@ -4,8 +4,9 @@ import ocelot.desktop.ColorScheme import ocelot.desktop.color.Color import ocelot.desktop.geometry.Vector2D import ocelot.desktop.node.Node +import ocelot.desktop.util.Message -class ComputerErrorMessageLabel(node: Node, override val text: String) extends Label { +class ComputerErrorMessageLabel(node: Node, override val text: Message) extends Label { override def isSmall: Boolean = true var alpha: Float = 1f diff --git a/src/main/scala/ocelot/desktop/ui/widget/DiskEditWindow.scala b/src/main/scala/ocelot/desktop/ui/widget/DiskEditWindow.scala index 488d4d7..502d973 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/DiskEditWindow.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/DiskEditWindow.scala @@ -7,9 +7,10 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.inventory.traits.DiskItem import ocelot.desktop.ui.layout.{AlignItems, LinearLayout} import ocelot.desktop.ui.widget.window.PanelWindow +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.SizeFormatting.formatSize import ocelot.desktop.util.animation.ColorAnimation -import ocelot.desktop.util.{DiskDriveAware, Orientation} +import ocelot.desktop.util.{DiskDriveAware, Message, Orientation} import totoro.ocelot.brain.entity.traits.{DiskManaged, DiskUnmanaged} import totoro.ocelot.brain.util.DyeColor @@ -32,7 +33,7 @@ class DiskEditWindow(item: DiskItem) extends PanelWindow { private def address: Option[String] = Option(item.entity.node).flatMap(node => Option(node.address)) - override protected def title: String = s"${item.diskKind} " + address.getOrElse("") + override protected def title: Message = s"${item.diskKind} " + address.getOrElse("") override protected def titleMaxLength: Int = 32 @@ -41,9 +42,9 @@ class DiskEditWindow(item: DiskItem) extends PanelWindow { children :+= new PaddingBox( new Label { - override def text: String = diskSize match { - case Some(used) => s"Used: ${formatSize(used)} / ${formatSize(diskCapacity)}" - case None => s"Capacity: ${formatSize(diskCapacity)}" + override def text: Message = diskSize match { + case Some(used) => s"${m"label.window.disk.used"}: ${formatSize(used)} / ${formatSize(diskCapacity)}" + case None => s"${m"label.window.disk.capacity"}: ${formatSize(diskCapacity)}" } }, Padding2D(bottom = 12), @@ -54,7 +55,7 @@ class DiskEditWindow(item: DiskItem) extends PanelWindow { override val layout = new LinearLayout(this, orientation = Orientation.Horizontal, alignItems = AlignItems.Center) - children :+= new Label("Label") + children :+= new Label(m"label.window.disk.label") children :+= new TextInput(item.label.getOrElse("")) { override def onConfirm(): Unit = { @@ -127,7 +128,7 @@ class DiskEditWindow(item: DiskItem) extends PanelWindow { override val layout = new LinearLayout(this, orientation = Orientation.Horizontal, gap = 8) children :+= new Button { - override def text: String = "Make read-only" + override def text: Message = m"label.window.disk.makeReadOnly" override def onClick(): Unit = item.lock() @@ -135,7 +136,7 @@ class DiskEditWindow(item: DiskItem) extends PanelWindow { } children :+= new Button { - override def text: String = s"Make ${if (isManaged) "unmanaged" else "managed"}" + override def text: Message = m"label.window.disk.make.${if (isManaged) "unmanaged" else "managed"}" override def onClick(): Unit = item.setManaged(!isManaged) diff --git a/src/main/scala/ocelot/desktop/ui/widget/Dropdown.scala b/src/main/scala/ocelot/desktop/ui/widget/Dropdown.scala new file mode 100644 index 0000000..f50a47e --- /dev/null +++ b/src/main/scala/ocelot/desktop/ui/widget/Dropdown.scala @@ -0,0 +1,19 @@ +package ocelot.desktop.ui.widget + +import ocelot.desktop.geometry.Vector2D +import ocelot.desktop.ui.UiHandler +import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} + +class Dropdown(options: List[String]) extends Button { + def onSelect(option: String): Unit = {} + + private var opened = false + + override def onClick(): Unit = { + opened = true + + val menu = new ContextMenu() + options.foreach(option => menu.addEntry(ContextMenuEntry(option) { onSelect(option) })) + UiHandler.root.contextMenus.open(menu, position + Vector2D(0, height)) + } +} diff --git a/src/main/scala/ocelot/desktop/ui/widget/Histogram.scala b/src/main/scala/ocelot/desktop/ui/widget/Histogram.scala index be259a4..6149396 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Histogram.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Histogram.scala @@ -3,12 +3,13 @@ package ocelot.desktop.ui.widget import ocelot.desktop.ColorScheme import ocelot.desktop.color.Color import ocelot.desktop.graphics.Graphics +import ocelot.desktop.util.Message import scala.collection.mutable.ArrayBuffer class Histogram extends Widget { var value = "N/A" - var title = "Hello world" + var title: Message = "Hello world" var history: ArrayBuffer[Float] = ArrayBuffer(50) private def drawBars(g: Graphics): Unit = { @@ -52,7 +53,7 @@ class Histogram extends Widget { val gridHeight = horizontalLineCount * cellSize // Text - drawText(g, x + gridWidth / 2, title) + drawText(g, x + gridWidth / 2, title.toString) // Horizontal lines for (i <- 0 until horizontalLineCount + 1) diff --git a/src/main/scala/ocelot/desktop/ui/widget/IconButton.scala b/src/main/scala/ocelot/desktop/ui/widget/IconButton.scala index 77150d3..b81d8bf 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/IconButton.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/IconButton.scala @@ -11,7 +11,7 @@ import ocelot.desktop.ui.widget.IconButton.{AnimationSpeed, Mode} import ocelot.desktop.ui.widget.tooltip.LabelTooltip import ocelot.desktop.ui.widget.traits.HoverAnimation import ocelot.desktop.util.animation.{ColorAnimation, ValueAnimation} -import ocelot.desktop.util.{DrawUtils, Spritesheet} +import ocelot.desktop.util.{DrawUtils, Message, Spritesheet} class IconButton( releasedIcon: String, @@ -22,7 +22,7 @@ class IconButton( mode: Mode = Mode.Regular, drawBackground: Boolean = false, padding: Float = 0, - tooltip: Option[String] = None, + tooltip: Option[Message] = None, darkenActiveColorFactor: Float = 0f, val model: IconButton.Model = IconButton.DefaultModel(false), ) extends Widget with MouseHandler with HoverHandler with HoverAnimation { diff --git a/src/main/scala/ocelot/desktop/ui/widget/InputDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/InputDialog.scala index 0d3edc9..ca0b5df 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/InputDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/InputDialog.scala @@ -4,10 +4,11 @@ import ocelot.desktop.audio.{ClickSoundSource, SoundSource} import ocelot.desktop.geometry.Padding2D import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.modal.ModalDialog -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} class InputDialog( - title: String, + title: Message, onConfirmed: String => Unit, initialText: String = "", inputValidator: String => Boolean = _ => true, @@ -32,7 +33,7 @@ class InputDialog( children :+= new PaddingBox( new Label { - override def text: String = title + override def text: Message = title }, Padding2D(bottom = 8), ) @@ -42,13 +43,13 @@ class InputDialog( children :+= new Widget { children :+= new Filler children :+= new Button { - override def text: String = "Cancel" + override def text: Message = m"button.cancel" override protected def clickSoundSource: ClickSoundSource = SoundSource.InterfaceClickLow override def onClick(): Unit = close() } children :+= new PaddingBox( new Button { - override def text: String = "Ok" + override def text: Message = m"button.ok" override def onClick(): Unit = { if (input.isInputValid) confirm() diff --git a/src/main/scala/ocelot/desktop/ui/widget/Label.scala b/src/main/scala/ocelot/desktop/ui/widget/Label.scala index e3ba28a..f7586ad 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Label.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Label.scala @@ -4,14 +4,15 @@ import ocelot.desktop.ColorScheme import ocelot.desktop.color.Color import ocelot.desktop.geometry.Size2D import ocelot.desktop.graphics.Graphics +import ocelot.desktop.util.Message -class Label(private val value: String = "", private val small: Boolean = false) extends Widget { - def text: String = value +class Label(private val value: Message = "", private val foreground: Color = ColorScheme("Label"), private val small: Boolean = false) extends Widget { + def text: Message = value def isSmall: Boolean = small - def color: Color = ColorScheme("Label") + def color: Color = foreground - private var textWidth = text.length * 8 - private def wideWidth(g: Graphics): Int = text.map(g.font.charWidth(_)).sum + private var textWidth = text.toString.length * 8 + private def wideWidth(g: Graphics): Int = text.toString.map(g.font.charWidth(_)).sum override def minimumSize: Size2D = Size2D(textWidth, if (isSmall) 8 else 16) override def maximumSize: Size2D = minimumSize.copy(width = Float.PositiveInfinity) @@ -23,7 +24,7 @@ class Label(private val value: String = "", private val small: Boolean = false) g.background = Color.Transparent g.foreground = color - g.text(position.x, position.y, text) + g.text(position.x, position.y, text.toString) g.setNormalFont() } diff --git a/src/main/scala/ocelot/desktop/ui/widget/MenuBar.scala b/src/main/scala/ocelot/desktop/ui/widget/MenuBar.scala index 9aabde2..a5e6c40 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/MenuBar.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/MenuBar.scala @@ -7,6 +7,7 @@ import ocelot.desktop.ui.event.KeyEvent import ocelot.desktop.ui.widget.contextmenu.{ContextMenuEntry, ContextMenuSubmenu} import ocelot.desktop.ui.widget.help.{AboutDialog, UpdateCheckerDialog} import ocelot.desktop.ui.widget.settings.SettingsDialog +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.{ColorScheme, OcelotDesktop} import org.lwjgl.input.Keyboard @@ -20,30 +21,30 @@ class MenuBar extends Widget { private def addEntry(w: Widget): Unit = entries.children :+= w addEntry(new MenuBarSubmenu( - "File", + m"label.menu.file", menu => { - menu.addEntry(ContextMenuEntry("New", IconSource.Plus) { OcelotDesktop.newWorkspace() }) - menu.addEntry(ContextMenuEntry("Open", IconSource.Folder) { OcelotDesktop.showOpenDialog() }) - menu.addEntry(ContextMenuEntry("Save", IconSource.Save) { OcelotDesktop.save() }) - menu.addEntry(ContextMenuEntry("Save as", IconSource.SaveAs) { OcelotDesktop.saveAs() }) + menu.addEntry(ContextMenuEntry(m"label.menu.file.new", IconSource.Plus) { OcelotDesktop.newWorkspace() }) + menu.addEntry(ContextMenuEntry(m"label.menu.file.open", IconSource.Folder) { OcelotDesktop.showOpenDialog() }) + menu.addEntry(ContextMenuEntry(m"label.menu.file.save", IconSource.Save) { OcelotDesktop.save() }) + menu.addEntry(ContextMenuEntry(m"label.menu.file.saveAs", IconSource.SaveAs) { OcelotDesktop.saveAs() }) menu.addSeparator() - menu.addEntry(ContextMenuEntry("Exit", IconSource.Cross, SoundSource.InterfaceClickLow) { OcelotDesktop.exit() }) + menu.addEntry(ContextMenuEntry(m"label.menu.file.exit", IconSource.Cross, SoundSource.InterfaceClickLow) { OcelotDesktop.exit() }) }, )) addEntry(new MenuBarSubmenu( - "Player", + m"label.menu.player", menu => { - menu.addEntry(ContextMenuEntry("Create new", IconSource.Plus) { OcelotDesktop.showAddPlayerDialog() }) + menu.addEntry(ContextMenuEntry(m"label.menu.player.new", IconSource.Plus) { OcelotDesktop.showAddPlayerDialog() }) menu.addSeparator() OcelotDesktop.players.foreach(player => { menu.addEntry(new ContextMenuSubmenu( s"${if (player == OcelotDesktop.players.head) "● " else " "}${player.nickname}", onClick = () => OcelotDesktop.selectPlayer(player.nickname), ) { - addEntry(ContextMenuEntry("Remove", IconSource.Delete, SoundSource.InterfaceClickLow) { + addEntry(ContextMenuEntry(m"label.menu.player.remove", IconSource.Delete, SoundSource.InterfaceClickLow) { OcelotDesktop.removePlayer(player.nickname) }) }) @@ -51,17 +52,17 @@ class MenuBar extends Widget { }, )) - addEntry(new MenuBarButton("Settings", () => new SettingsDialog().show())) + addEntry(new MenuBarButton(m"label.menu.settings", () => new SettingsDialog().show())) addEntry(new MenuBarSubmenu( - "Help", + m"label.menu.help", menu => { - menu.addEntry(ContextMenuEntry("Check for Updates", IconSource.Help) { + menu.addEntry(ContextMenuEntry(m"label.menu.help.updates", IconSource.Help) { new UpdateCheckerDialog().show() }) menu.addSeparator() - menu.addEntry(ContextMenuEntry("Manual", IconSource.Book) {}.setEnabled(false)) - menu.addEntry(ContextMenuEntry("About", IconSource.Ocelot) { new AboutDialog().show() }) + menu.addEntry(ContextMenuEntry(m"label.menu.help.manual", IconSource.Book) {}.setEnabled(false)) + menu.addEntry(ContextMenuEntry(m"label.menu.help.about", IconSource.Ocelot) { new AboutDialog().show() }) }, )) diff --git a/src/main/scala/ocelot/desktop/ui/widget/MenuBarButton.scala b/src/main/scala/ocelot/desktop/ui/widget/MenuBarButton.scala index 25235d4..66877d2 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/MenuBarButton.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/MenuBarButton.scala @@ -7,15 +7,16 @@ import ocelot.desktop.geometry.{Padding2D, Size2D} import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler} import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent} +import ocelot.desktop.util.Message import ocelot.desktop.util.animation.ColorAnimation -class MenuBarButton(label: String, handler: () => Unit = () => {}) extends Widget with MouseHandler with HoverHandler { +class MenuBarButton(label: Message, handler: () => Unit = () => {}) extends Widget with MouseHandler with HoverHandler { val colorAnimation: ColorAnimation = new ColorAnimation(ColorScheme("TitleBarBackground"), 0.6f) children :+= new PaddingBox( new Label { - override def text: String = label + override def text: Message = label override def color: Color = ColorScheme("TitleBarButtonForeground") override def maximumSize: Size2D = minimumSize }, diff --git a/src/main/scala/ocelot/desktop/ui/widget/MenuBarSubmenu.scala b/src/main/scala/ocelot/desktop/ui/widget/MenuBarSubmenu.scala index 6bc7e83..c24616b 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/MenuBarSubmenu.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/MenuBarSubmenu.scala @@ -3,8 +3,9 @@ package ocelot.desktop.ui.widget import ocelot.desktop.geometry.Vector2D import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.widget.contextmenu.ContextMenu +import ocelot.desktop.util.Message -class MenuBarSubmenu(label: String, setup: ContextMenu => Unit) extends MenuBarButton(label) { +class MenuBarSubmenu(label: Message, setup: ContextMenu => Unit) extends MenuBarButton(label) { private var menuOpened = false override def onMouseLeave(): Unit = { diff --git a/src/main/scala/ocelot/desktop/ui/widget/ScreenAspectRatioDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/ScreenAspectRatioDialog.scala index 09e9fc2..196ff95 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/ScreenAspectRatioDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/ScreenAspectRatioDialog.scala @@ -5,14 +5,15 @@ import ocelot.desktop.geometry.Padding2D import ocelot.desktop.node.nodes.ScreenNode import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.modal.ModalDialog -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} class ScreenAspectRatioDialog(screenNode: ScreenNode) extends ModalDialog { children :+= new PaddingBox( new Widget { override val layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= new PaddingBox(new Label("Set aspect ratio"), Padding2D(bottom = 16)) + children :+= new PaddingBox(new Label(m"label.menu.setAspectRatio"), Padding2D(bottom = 16)) private var typedWidth: Option[Int] = Some(screenNode.screen.aspectRatio._1.toInt) private var typedHeight: Option[Int] = Some(screenNode.screen.aspectRatio._2.toInt) @@ -55,7 +56,7 @@ class ScreenAspectRatioDialog(screenNode: ScreenNode) extends ModalDialog { ) } - children :+= new Label("Width:") + children :+= new Label(m"label.dialog.aspect.width") addInput( 8, @@ -63,7 +64,7 @@ class ScreenAspectRatioDialog(screenNode: ScreenNode) extends ModalDialog { value => typedWidth = value, ) - children :+= new Label("Height:") + children :+= new Label(m"label.dialog.aspect.height") addInput( 6, @@ -76,7 +77,7 @@ class ScreenAspectRatioDialog(screenNode: ScreenNode) extends ModalDialog { children :+= new PaddingBox( new Button { - override def text: String = "Cancel" + override def text: Message = m"button.cancel" override protected def clickSoundSource: ClickSoundSource = SoundSource.InterfaceClickLow override def onClick(): Unit = close() }, @@ -84,7 +85,7 @@ class ScreenAspectRatioDialog(screenNode: ScreenNode) extends ModalDialog { ) val confirmButton: Button = new Button { - override def text: String = "Apply" + override def text: Message = m"button.apply" override def onClick(): Unit = confirm() override def enabled: Boolean = canConfirm } diff --git a/src/main/scala/ocelot/desktop/ui/widget/Slider.scala b/src/main/scala/ocelot/desktop/ui/widget/Slider.scala index 8e30473..82c0aa4 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Slider.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Slider.scala @@ -9,9 +9,9 @@ import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.event.handlers.MouseHandler import ocelot.desktop.ui.event.{ClickEvent, DragEvent, MouseEvent} import ocelot.desktop.ui.widget.traits.HoverAnimation -import ocelot.desktop.util.DrawUtils +import ocelot.desktop.util.{DrawUtils, Message} -class Slider(var value: Float, val text: String, val snapPoints: Int = 0) +class Slider(var value: Float, val text: Message, val snapPoints: Int = 0) extends Widget with MouseHandler with HoverAnimation { override protected val hoverAnimationColorDefault: Color = ColorScheme("SliderBackground") diff --git a/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala b/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala index 7153bd0..aea3d0d 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala @@ -9,7 +9,7 @@ import ocelot.desktop.ui.event.handlers.MouseHandler import ocelot.desktop.ui.event.sources.KeyEvents import ocelot.desktop.ui.event.{ClickEvent, KeyEvent, MouseEvent} import ocelot.desktop.ui.widget.traits.HoverAnimation -import ocelot.desktop.util.DrawUtils +import ocelot.desktop.util.{DrawUtils, Message} import ocelot.desktop.util.animation.ColorAnimation import org.lwjgl.input.Keyboard @@ -34,7 +34,7 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w def text_=(value: String): Unit = chars = value.toCharArray protected var placeholder: Array[Char] = "".toCharArray - def placeholder_=(value: String): Unit = placeholder = value.toCharArray + def placeholder_=(value: Message): Unit = placeholder = value.toString.toCharArray private var cursorPos = 0 private var cursorOffset = 0f diff --git a/src/main/scala/ocelot/desktop/ui/widget/TunnelDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/TunnelDialog.scala index e044fc4..4b8b119 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/TunnelDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/TunnelDialog.scala @@ -6,7 +6,8 @@ import ocelot.desktop.geometry.{Padding2D, Size2D} import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.modal.ModalDialog -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} import java.util.UUID @@ -31,7 +32,7 @@ class TunnelDialog( new Widget { override val layout = new LinearLayout(this, orientation = Orientation.Vertical, gap = 8) - children :+= new PaddingBox(new Label("Set channel name/code"), Padding2D(bottom = 8)) + children :+= new PaddingBox(new Label(m"label.dialog.tunnel"), Padding2D(bottom = 8)) children :+= new Widget { children :+= input @@ -42,7 +43,7 @@ class TunnelDialog( drawBackground = true, releasedColor = ColorScheme("ButtonForeground"), padding = 3.5f, - tooltip = Some("Generate random channel name"), + tooltip = Some(m"label.dialog.tunnel.random"), ) { override def onPressed(): Unit = input.text = UUID.randomUUID().toString }, @@ -55,7 +56,7 @@ class TunnelDialog( releasedColor = ColorScheme("ButtonForeground"), pressedColor = ColorScheme("ButtonConfirm"), padding = 3.5f, - tooltip = Some("Copy channel name to the clipboard"), + tooltip = Some(m"label.dialog.tunnel.copy"), ) { override def onPressed(): Unit = UiHandler.clipboard = input.text } @@ -64,13 +65,13 @@ class TunnelDialog( children :+= new Widget { children :+= new Filler children :+= new Button { - override def text: String = "Cancel" + override def text: Message = m"button.cancel" override protected def clickSoundSource: ClickSoundSource = SoundSource.InterfaceClickLow override def onClick(): Unit = close() } children :+= new PaddingBox( new Button { - override def text: String = "Ok" + override def text: Message = m"button.ok" override def onClick(): Unit = { if (input.isInputValid) confirm() diff --git a/src/main/scala/ocelot/desktop/ui/widget/Viewport3DWidget.scala b/src/main/scala/ocelot/desktop/ui/widget/Viewport3DWidget.scala index 58030b1..e701ca0 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/Viewport3DWidget.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/Viewport3DWidget.scala @@ -10,6 +10,7 @@ import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler} import ocelot.desktop.ui.event.sources.KeyEvents import ocelot.desktop.ui.event.{DragEvent, MouseEvent, ScrollEvent} import ocelot.desktop.ui.layout.{CopyLayout, Layout, LinearLayout} +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.{DrawUtils, Orientation} abstract class Viewport3DWidget extends Widget with MouseHandler with HoverHandler { @@ -150,8 +151,8 @@ abstract class Viewport3DWidget extends Widget with MouseHandler with HoverHandl scene = createScene() if (isHovered) { - root.get.statusBar.addMouseEntry("icons/LMB", "Rotate view") - root.get.statusBar.addKeyMouseEntry("icons/LMB", "Shift", "Pan view") + root.get.statusBar.addMouseEntry("icons/LMB", m"label.bar.rotateView") + root.get.statusBar.addKeyMouseEntry("icons/LMB", "Shift", m"label.bar.panView") } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/WorkspaceView.scala b/src/main/scala/ocelot/desktop/ui/widget/WorkspaceView.scala index e040ae7..ba5014a 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/WorkspaceView.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/WorkspaceView.scala @@ -12,6 +12,7 @@ import ocelot.desktop.ui.event.sources.KeyEvents import ocelot.desktop.ui.layout.{CopyLayout, Layout} import ocelot.desktop.ui.widget.WorkspaceView.NodeLoadException import ocelot.desktop.ui.widget.window.{NodeSelector, ProfilerWindow, WindowPool} +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.ReflectionUtils.findUnaryConstructor import ocelot.desktop.util.animation.ValueAnimation import ocelot.desktop.util.{DrawUtils, Logging, Persistable} @@ -608,19 +609,19 @@ class WorkspaceView extends Widget with Persistable with MouseHandler with Hover nodes.foreach(_.update()) if (isHovered) { - root.get.statusBar.addKeyEntry("HOME", "Reset camera") + root.get.statusBar.addKeyEntry("HOME", m"label.bar.resetCamera") } if (isHovered || nodes.exists(_.isHovered)) { - root.get.statusBar.addKeyEntry("CTRL", "Show grid") + root.get.statusBar.addKeyEntry("CTRL", m"label.bar.showGrid") } if (isHovered && newConnection.isEmpty) { if (nodeSelector.isClosed) - root.get.statusBar.addMouseEntry("icons/LMB", "Add node") + root.get.statusBar.addMouseEntry("icons/LMB", m"label.bar.addNode") else - root.get.statusBar.addMouseEntry("icons/LMB", "Close selector") - root.get.statusBar.addMouseEntry("icons/DragLMB", "Move camera") + root.get.statusBar.addMouseEntry("icons/LMB", m"label.bar.closeSelector") + root.get.statusBar.addMouseEntry("icons/DragLMB", m"label.bar.moveCamera") } } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/card/Redstone1Window.scala b/src/main/scala/ocelot/desktop/ui/widget/card/Redstone1Window.scala index b8bc13b..eaaf803 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/card/Redstone1Window.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/card/Redstone1Window.scala @@ -4,7 +4,8 @@ import ocelot.desktop.geometry.{Padding2D, Size2D} import ocelot.desktop.ui.layout.{AlignItems, Layout, LinearLayout} import ocelot.desktop.ui.widget.window.PanelWindow import ocelot.desktop.ui.widget.{Knob, Label, PaddingBox, Widget} -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} import totoro.ocelot.brain.entity.Redstone import totoro.ocelot.brain.util.{Direction, DyeColor} @@ -21,18 +22,18 @@ class Redstone1Window(card: Redstone.Tier1) extends PanelWindow { override def output: Int = card.redstoneOutput(side.id) min 15 max 0 } - override protected def title: String = s"Redstone I/O ${card.node.address}" + override protected def title: Message = s"${m"component.redstoneIO"} ${card.node.address}" override protected def titleMaxLength: Int = 26 - private def redstoneBlock(side: Direction.Value, name: String) = new PaddingBox( + private def redstoneBlock(side: Direction.Value, name: Message) = new PaddingBox( new Widget { override protected val layout: Layout = new LinearLayout(this, Orientation.Horizontal, alignItems = AlignItems.Center) children :+= new PaddingBox( new Label { - override def text: String = name + override def text: Message = name override def maximumSize: Size2D = minimumSize }, @@ -47,18 +48,18 @@ class Redstone1Window(card: Redstone.Tier1) extends PanelWindow { setInner(new Widget { children :+= new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= redstoneBlock(Direction.Down, "Bottom") - children :+= redstoneBlock(Direction.Up, " Top") + children :+= redstoneBlock(Direction.Down, m"direction.bottom") + children :+= redstoneBlock(Direction.Up, m"direction.top") } children :+= new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= redstoneBlock(Direction.Back, " Back") - children :+= redstoneBlock(Direction.Front, "Front") + children :+= redstoneBlock(Direction.Back, m"direction.back") + children :+= redstoneBlock(Direction.Front, m"direction.front") } children :+= new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= redstoneBlock(Direction.Right, "Right") - children :+= redstoneBlock(Direction.Left, " Left") + children :+= redstoneBlock(Direction.Right, m"direction.right") + children :+= redstoneBlock(Direction.Left, m"direction.left") } }) } diff --git a/src/main/scala/ocelot/desktop/ui/widget/card/Redstone2Window.scala b/src/main/scala/ocelot/desktop/ui/widget/card/Redstone2Window.scala index 35ebd4e..4d3f687 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/card/Redstone2Window.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/card/Redstone2Window.scala @@ -4,7 +4,8 @@ import ocelot.desktop.geometry.{Padding2D, Size2D} import ocelot.desktop.ui.layout.{AlignItems, Layout, LinearLayout} import ocelot.desktop.ui.widget.window.PanelWindow import ocelot.desktop.ui.widget.{Knob, Label, PaddingBox, Widget} -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} import totoro.ocelot.brain.entity.Redstone import totoro.ocelot.brain.util.{Direction, DyeColor} @@ -21,17 +22,17 @@ class Redstone2Window(card: Redstone.Tier2) extends PanelWindow { override def output: Int = card.bundledRedstoneOutput(side.id)(col) min 15 max 0 } - override protected def title: String = s"Bundled I/O ${card.node.address}" + override protected def title: Message = s"${m"component.bundledIO"} ${card.node.address}" override protected def titleMaxLength: Int = 36 - private def bundledBlock(side: Direction.Value, name: String) = new PaddingBox( + private def bundledBlock(side: Direction.Value, name: Message) = new PaddingBox( new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical, alignItems = AlignItems.Center) children :+= new Label { - override def text: String = name + override def text: Message = name override def maximumSize: Size2D = minimumSize } @@ -66,18 +67,18 @@ class Redstone2Window(card: Redstone.Tier2) extends PanelWindow { setInner(new Widget { children :+= new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= bundledBlock(Direction.Down, "Bottom") - children :+= bundledBlock(Direction.Up, "Top") + children :+= bundledBlock(Direction.Down, m"direction.bottom") + children :+= bundledBlock(Direction.Up, m"direction.top") } children :+= new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= bundledBlock(Direction.Back, "Back") - children :+= bundledBlock(Direction.Front, "Front") + children :+= bundledBlock(Direction.Back, m"direction.back") + children :+= bundledBlock(Direction.Front, m"direction.front") } children :+= new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) - children :+= bundledBlock(Direction.Right, "Right") - children :+= bundledBlock(Direction.Left, "Left") + children :+= bundledBlock(Direction.Right, m"direction.right") + children :+= bundledBlock(Direction.Left, m"direction.left") } }) } diff --git a/src/main/scala/ocelot/desktop/ui/widget/card/SoundCardWindow.scala b/src/main/scala/ocelot/desktop/ui/widget/card/SoundCardWindow.scala index 956b2cf..54c57b4 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/card/SoundCardWindow.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/card/SoundCardWindow.scala @@ -8,7 +8,8 @@ import ocelot.desktop.ui.layout.{Layout, LinearLayout} import ocelot.desktop.ui.widget.card.SoundCardWindow._ import ocelot.desktop.ui.widget.window.{PanelWindow, Window} import ocelot.desktop.ui.widget.{Oscilloscope, PaddingBox, Widget} -import ocelot.desktop.util.{DrawUtils, Orientation} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{DrawUtils, Message, Orientation} import totoro.ocelot.brain.Settings import totoro.ocelot.brain.entity.sound_card._ import totoro.ocelot.brain.event.{EventBus, SoundCardAudioEvent} @@ -40,7 +41,7 @@ class SoundCardWindow(card: SoundCard) extends PanelWindow { private var eventSub: Option[EventBus.Subscription] = None - override protected def title: String = s"Sound Card ${card.node.address}" + override protected def title: Message = s"${m"component.soundCard"} ${card.node.address}" override protected def titleMaxLength: Int = 80 diff --git a/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuEntry.scala b/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuEntry.scala index 459c324..32abdde 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuEntry.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuEntry.scala @@ -9,11 +9,12 @@ import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler} import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent} import ocelot.desktop.ui.layout.{AlignItems, Layout, LinearLayout} import ocelot.desktop.ui.widget._ +import ocelot.desktop.util.Message import ocelot.desktop.util.animation.ValueAnimation import ocelot.desktop.util.animation.easing.{EaseInQuad, EaseOutQuad} class ContextMenuEntry( - label: String, + label: Message, icon: Option[ContextMenuIcon] = None, onClick: () => Unit = () => {}, sound: ClickSoundSource = SoundSource.InterfaceClick, @@ -55,7 +56,7 @@ class ContextMenuEntry( children :+= new PaddingBox( new Label { - override def text: String = label + override def text: Message = label override def color: Color = ColorScheme("ContextMenuText") }, Padding2D(top = 5f, bottom = 5f), @@ -135,7 +136,7 @@ class ContextMenuEntry( object ContextMenuEntry { def apply( - label: String, + label: Message, icon: Option[ContextMenuIcon] = None, )(onClick: => Unit): ContextMenuEntry = { new ContextMenuEntry( @@ -146,7 +147,7 @@ object ContextMenuEntry { } def apply( - label: String + label: Message )(onClick: => Unit): ContextMenuEntry = { new ContextMenuEntry( label, @@ -156,7 +157,7 @@ object ContextMenuEntry { } def apply( - label: String, + label: Message, icon: IconSource, )(onClick: => Unit): ContextMenuEntry = { new ContextMenuEntry( @@ -167,7 +168,7 @@ object ContextMenuEntry { } def apply( - label: String, + label: Message, icon: IconSource, sound: ClickSoundSource, )(onClick: => Unit): ContextMenuEntry = { diff --git a/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuSubmenu.scala b/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuSubmenu.scala index 9f1e842..a31cdf4 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuSubmenu.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/contextmenu/ContextMenuSubmenu.scala @@ -6,9 +6,10 @@ import ocelot.desktop.geometry.Vector2D import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.event.HoverEvent +import ocelot.desktop.util.Message class ContextMenuSubmenu( - label: String, + label: Message, icon: Option[ContextMenuIcon] = None, onClick: () => Unit = () => {}, ) extends ContextMenuEntry( diff --git a/src/main/scala/ocelot/desktop/ui/widget/help/AboutDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/help/AboutDialog.scala index 3b4d364..3875291 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/help/AboutDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/help/AboutDialog.scala @@ -9,7 +9,8 @@ import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.{Button, Filler, Label, PaddingBox, Widget} import ocelot.desktop.ui.widget.modal.ModalDialog import ocelot.desktop.ui.widget.tooltip.LabelTooltip -import ocelot.desktop.util.{DrawUtils, Orientation, Spritesheet} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{DrawUtils, Message, Orientation, Spritesheet} import java.awt.Desktop import java.net.URI @@ -37,8 +38,8 @@ class AboutDialog extends ModalDialog { children :++= Seq( new PaddingBox(AboutLabel("OCELOT DESKTOP"), Padding2D(top = 32)), - new PaddingBox(AboutLabel(" OpenComputers Emulator", isSmall = true), Padding2D(bottom = 16)), - AboutLabel(s"Version: ${BuildInfo.version}"), + new PaddingBox(AboutLabel(m"label.dialog.about.subtitle", isSmall = true), Padding2D(bottom = 16, left = 16)), + AboutLabel(s"${m"label.dialog.about.version"}: ${BuildInfo.version}"), new PaddingBox(AboutLabel(s" (${BuildInfo.commit.take(7)})", isSmall = true), Padding2D(bottom = 16)), AboutLabel(s"MIT License (c) ${Year.now.getValue} Ocelot Dev Team"), AboutLabel(" OpenComputers (C) 2013-2015, MIT, Florian \"Sangar\" Nücke", isSmall = true), @@ -61,13 +62,13 @@ class AboutDialog extends ModalDialog { } if (Desktop.isDesktopSupported) { - children :+= new PaddingBox(new AboutButton("Website", WebsiteURL), Padding2D(right = 8)) + children :+= new PaddingBox(new AboutButton(m"label.dialog.about.website", WebsiteURL), Padding2D(right = 8)) children :+= new PaddingBox(new AboutButton("IRC", IrcURL), Padding2D(right = 8)) children :+= new PaddingBox(new AboutButton("Discord", DiscordURL), Padding2D(right = 20)) } children :+= new Button { - override def text: String = "Ok" + override def text: Message = m"button.ok" override def onClick(): Unit = close() override protected def colorScheme: ColorScheme = customButtonColorScheme } @@ -102,19 +103,19 @@ class AboutDialog extends ModalDialog { scheme } - private class AboutButton(val label: String, val url: String) extends Button(new LabelTooltip(url)) { - override def text: String = label + private class AboutButton(val label: Message, val url: String) extends Button(new LabelTooltip(url)) { + override def text: Message = label override def onClick(): Unit = Desktop.getDesktop.browse(new URI(url)) override protected def colorScheme: ColorScheme = customButtonColorScheme } - private class AboutLabel(private val _text: String, private val _isSmall: Boolean = false) extends Label { - override def text: String = _text + private class AboutLabel(private val _text: Message, private val _isSmall: Boolean = false) extends Label { + override def text: Message = _text override def isSmall: Boolean = _isSmall override def color: Color = if (isSmall) ColorScheme("AboutForegroundSubtle") else ColorScheme("AboutForeground") } private object AboutLabel { - def apply(text: String, isSmall: Boolean = false): AboutLabel = new AboutLabel(text, isSmall) + def apply(text: Message, isSmall: Boolean = false): AboutLabel = new AboutLabel(text, isSmall) } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/help/UpdateCheckerDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/help/UpdateCheckerDialog.scala index 2dd31b8..193790c 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/help/UpdateCheckerDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/help/UpdateCheckerDialog.scala @@ -7,8 +7,9 @@ import ocelot.desktop.graphics.IconSource import ocelot.desktop.ui.layout.{Layout, LinearLayout} import ocelot.desktop.ui.widget.{Button, Filler, Icon, Label, PaddingBox, Widget} import ocelot.desktop.ui.widget.modal.ModalDialog +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.Orientation.Vertical -import ocelot.desktop.util.{Logging, OcelotOnlineAPI} +import ocelot.desktop.util.{Logging, Message, OcelotOnlineAPI} import java.awt.Desktop import java.net.URI @@ -35,7 +36,7 @@ class UpdateCheckerDialog extends ModalDialog with Logging { override def maximumSize: Size2D = Size2D(Float.PositiveInfinity, 1) } children :+= new Button { - override def text: String = "Ok" + override def text: Message = m"button.ok" override def onClick(): Unit = close() } } @@ -44,7 +45,7 @@ class UpdateCheckerDialog extends ModalDialog with Logging { ) // loading screen - container.children :+= new Label("Checking development news...") + container.children :+= new Label(m"label.dialog.updates.checking") container.children :+= new PaddingBox(new Icon(IconSource.Loading, color = ColorScheme("Label")), Padding2D(16, 0, 0, 90)) @@ -55,16 +56,16 @@ class UpdateCheckerDialog extends ModalDialog with Logging { setContainerChildren(ArraySeq.empty) if (version.releaseVersion.isDefined) { val newReleaseVersion = version.releaseVersion.get.drop(1) - container.children :+= new Label(s"Release: ${BuildInfo.version} ▸ ${newReleaseVersion}") + container.children :+= new Label(s"${m"label.dialog.updates.release"}: ${BuildInfo.version} ▸ $newReleaseVersion") if (newReleaseVersion == BuildInfo.version) { - container.children :+= new PaddingBox(new Label("Up to date!", small = true), Padding2D(6)) + container.children :+= new PaddingBox(new Label(m"label.dialog.updates.upToDate", small = true), Padding2D(6)) } else { - container.children :+= new PaddingBox(new Label("New release version is available:", small = true), + container.children :+= new PaddingBox(new Label(m"label.dialog.updates.releaseUpdateAvailable", foreground = ColorScheme("LabelUpdateAvailable"), small = true), Padding2D(6)) if (Desktop.isDesktopSupported) { container.children :+= new PaddingBox( new Button { - override def text: String = "Download" + override def text: Message = m"button.download" override def onClick(): Unit = Desktop.getDesktop.browse(new URI("https://ocelot.fomalhaut.me/desktop")) }, @@ -76,7 +77,7 @@ class UpdateCheckerDialog extends ModalDialog with Logging { } } } else { - container.children :+= new Label("Release: N\\A") + container.children :+= new Label(s"${m"label.dialog.updates.release"}: N\\A") } container.children :+= new Filler { @@ -85,21 +86,21 @@ class UpdateCheckerDialog extends ModalDialog with Logging { if (version.devId.isDefined && version.devDate.isDefined) { val newDevVersion = version.devId.get.take(7) - container.children :+= new Label(s"Dev build: ${BuildInfo.commit.take(7)} ▸ ${newDevVersion}") + container.children :+= new Label(s"${m"label.dialog.updates.devBuild"}: ${BuildInfo.commit.take(7)} ▸ $newDevVersion") if (BuildInfo.commit.take(7) == newDevVersion) { - container.children :+= new PaddingBox(new Label("Up to date!", small = true), Padding2D(6)) + container.children :+= new PaddingBox(new Label(m"label.dialog.updates.upToDate", small = true), Padding2D(6)) } else { container.children :+= new PaddingBox( new Label( - s"New dev build from ${version.devDate.get.withZoneSameInstant(ZoneId.systemDefault()).format(dateFormat)}:", - small = true, + s"${m"label.dialog.updates.devUpdateAvailable"} ${version.devDate.get.withZoneSameInstant(ZoneId.systemDefault()).format(dateFormat)}!", + foreground = ColorScheme("LabelUpdateAvailable"), small = true, ), Padding2D(4), ) if (Desktop.isDesktopSupported) { container.children :+= new PaddingBox( new Button { - override def text: String = "Download" + override def text: Message = m"button.download" override def onClick(): Unit = Desktop.getDesktop.browse(new URI("https://ocelot.fomalhaut.me/desktop")) }, @@ -111,17 +112,19 @@ class UpdateCheckerDialog extends ModalDialog with Logging { } } } else { - container.children :+= new Label("Dev build: N\\A") + container.children :+= new Label(s"${m"label.dialog.updates.devBuild"}: N\\A") } case Failure(exception) => logger.error("Cannot download information about new Ocelot version!", exception) - val message = "Ocelot website is unavailable...\n" + - s"($exception)\n" + - "Check the log file for a full stacktrace." - setContainerChildren(ArraySeq.unsafeWrapArray(message.split('\n').map(line => { + val messages: Array[Message] = Array( + m"label.dialog.updates.unavailable", + s"(${exception.getMessage.split('\n').head})", + m"label.dialog.updates.checkLogs" + ) + setContainerChildren(ArraySeq.unsafeWrapArray(messages.map(message => { new Label { - override def text: String = line + override def text: Message = message } }))) } diff --git a/src/main/scala/ocelot/desktop/ui/widget/modal/notification/NotificationDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/modal/notification/NotificationDialog.scala index 5736d83..3f2adbe 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/modal/notification/NotificationDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/modal/notification/NotificationDialog.scala @@ -8,9 +8,10 @@ import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.modal.ModalDialog import ocelot.desktop.ui.widget.modal.notification.NotificationType.NotificationType -import ocelot.desktop.util.{DrawUtils, Orientation} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{DrawUtils, Message, Orientation} -class NotificationDialog(message: String, notificationType: NotificationType = NotificationType.Warning) +class NotificationDialog(message: Message, notificationType: NotificationType = NotificationType.Warning) extends ModalDialog { private val buttonsLayout: Widget = new Widget { override val layout: LinearLayout = new LinearLayout(this, gap = 15) @@ -49,10 +50,9 @@ class NotificationDialog(message: String, notificationType: NotificationType = N override val layout: LinearLayout = new LinearLayout(this, orientation = Orientation.Vertical, gap = 5) - children :++= message.split('\n').map(line => { + children :++= message.toStringMultiline.map(line => { new Label { - override def text: String = line - + override def text: Message = line override def color: Color = ColorScheme("ContextMenuText") } }) @@ -76,11 +76,11 @@ class NotificationDialog(message: String, notificationType: NotificationType = N drawChildren(g) } - def addButton(buttonText: String)(onClick: => Unit): NotificationDialog = { + def addButton(buttonText: Message)(onClick: => Unit): NotificationDialog = { val callback = onClick _ buttonsLayout.children :+= new Button { - override def text: String = buttonText + override def text: Message = buttonText override def onClick(): Unit = callback() } @@ -88,7 +88,7 @@ class NotificationDialog(message: String, notificationType: NotificationType = N this } - def addCloseButton(buttonText: String = "Ok"): NotificationDialog = addButton(buttonText) { + def addCloseButton(buttonText: Message = m"button.ok"): NotificationDialog = addButton(buttonText) { close() } diff --git a/src/main/scala/ocelot/desktop/ui/widget/settings/SettingsDialog.scala b/src/main/scala/ocelot/desktop/ui/widget/settings/SettingsDialog.scala index 7d71513..51710a9 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/settings/SettingsDialog.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/settings/SettingsDialog.scala @@ -8,7 +8,8 @@ import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.modal.ModalDialog import ocelot.desktop.ui.widget.verticalmenu.{VerticalMenu, VerticalMenuButton} import ocelot.desktop.ui.widget.{Button, PaddingBox, Widget} -import ocelot.desktop.util.{Orientation, SettingsData} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation, SettingsData} class SettingsDialog extends ModalDialog { private val settingsBackup = new SettingsData(Settings.get) @@ -37,7 +38,7 @@ class SettingsDialog extends ModalDialog { } children :+= new Button { - override def text: String = "Cancel" + override def text: Message = m"button.cancel" override protected def clickSoundSource: ClickSoundSource = SoundSource.InterfaceClickLow override def onClick(): Unit = { resetSettings() @@ -47,7 +48,7 @@ class SettingsDialog extends ModalDialog { children :+= new PaddingBox( new Button { - override def text: String = "Ok" + override def text: Message = m"button.ok" override def onClick(): Unit = close() }, diff --git a/src/main/scala/ocelot/desktop/ui/widget/settings/SettingsTab.scala b/src/main/scala/ocelot/desktop/ui/widget/settings/SettingsTab.scala index ab80c6e..9ff4cd7 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/settings/SettingsTab.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/settings/SettingsTab.scala @@ -3,13 +3,13 @@ package ocelot.desktop.ui.widget.settings import ocelot.desktop.graphics.IconSource import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.Widget -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.{Message, Orientation} trait SettingsTab extends Widget { override protected val layout = new LinearLayout(this, orientation = Orientation.Vertical) val icon: IconSource - val label: String + val label: Message /** Callback that will be called by the parent Settings dialog when settings were changed * from outside the tab (for example user pressed "Cancel" and they were reset). diff --git a/src/main/scala/ocelot/desktop/ui/widget/settings/SoundSettingsTab.scala b/src/main/scala/ocelot/desktop/ui/widget/settings/SoundSettingsTab.scala index 798fc4e..65fe34f 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/settings/SoundSettingsTab.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/settings/SoundSettingsTab.scala @@ -5,15 +5,16 @@ import ocelot.desktop.geometry.Size2D import ocelot.desktop.graphics.IconSource import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.{Checkbox, Slider} -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} class SoundSettingsTab extends SettingsTab { override val layout = new LinearLayout(this, orientation = Orientation.Vertical, gap = 8) override val icon: IconSource = IconSource.SettingsSound - override val label: String = "Sound" + override val label: Message = m"label.settings.sound" - private def addSlider(name: String, value: Float, valueSetter: Float => Unit): Unit = { + private def addSlider(name: Message, value: Float, valueSetter: Float => Unit): Unit = { children :+= new Slider(value, name) { override def minimumSize: Size2D = Size2D(512, 24) @@ -25,36 +26,36 @@ class SoundSettingsTab extends SettingsTab { } addSlider( - "Master volume", + m"label.settings.sound.master", Settings.get.volumeMaster, Settings.get.volumeMaster = _, ) addSlider( - "Music blocks volume", + m"label.settings.sound.music", Settings.get.volumeRecords, Settings.get.volumeRecords = _, ) addSlider( - "Environment volume", + m"label.settings.sound.environment", Settings.get.volumeEnvironment, Settings.get.volumeEnvironment = _, ) addSlider( - "Beep volume", + m"label.settings.sound.beep", Settings.get.volumeBeep, Settings.get.volumeBeep = _, ) addSlider( - "Interface volume", + m"label.settings.sound.ui", Settings.get.volumeInterface, Settings.get.volumeInterface = _, ) - children :+= new Checkbox("Use positional sound", Settings.get.soundPositional) { + children :+= new Checkbox(m"label.settings.sound.positional", Settings.get.soundPositional) { override def minimumSize: Size2D = Size2D(512, 24) override def onValueChanged(value: Boolean): Unit = { diff --git a/src/main/scala/ocelot/desktop/ui/widget/settings/SystemSettingsTab.scala b/src/main/scala/ocelot/desktop/ui/widget/settings/SystemSettingsTab.scala index ae7941a..b139e9f 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/settings/SystemSettingsTab.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/settings/SystemSettingsTab.scala @@ -7,7 +7,8 @@ import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.modal.notification.{NotificationDialog, NotificationType} import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.tooltip.LabelTooltip -import ocelot.desktop.util.{FileUtils, Logging, OcelotPaths, Orientation} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{FileUtils, Logging, Message, OcelotPaths, Orientation} import ocelot.desktop.{ColorScheme, OcelotDesktop, Settings} import java.io.File @@ -18,7 +19,7 @@ class SystemSettingsTab extends SettingsTab with Logging { private val OpenComputersConfigResource = "/application.conf" override val icon: IconSource = IconSource.SettingsSystem - override val label: String = "System" + override val label: Message = m"label.settings.system" override def applySettings(): Unit = { if (Settings.get.autosave) { @@ -26,10 +27,10 @@ class SystemSettingsTab extends SettingsTab with Logging { } } - children :+= new PaddingBox(new Label("Set OpenComputers configuration file path:"), Padding2D(bottom = 8)) + children :+= new PaddingBox(new Label(m"label.settings.system.confPath"), Padding2D(bottom = 8)) private val textInput: TextInput = new TextInput(Settings.get.brainCustomConfigPath.getOrElse("")) { - if (Settings.get.brainCustomConfigPath.isEmpty) placeholder = "not set / using default OpenComputers configuration" + if (Settings.get.brainCustomConfigPath.isEmpty) placeholder = m"label.settings.system.confPathPlaceholder" override def onInput(text: String): Unit = { Settings.get.brainCustomConfigPath = Some(text) restartWarning.isVisible = true @@ -49,7 +50,7 @@ class SystemSettingsTab extends SettingsTab with Logging { pressedColor = ColorScheme("ButtonForegroundPressed"), drawBackground = true, padding = 3.5f, - tooltip = Some("Search for OpenComputers configuration file"), + tooltip = Some(m"label.settings.system.confSearch"), ) { override def onPressed(): Unit = { OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.FILES_ONLY) { @@ -64,19 +65,19 @@ class SystemSettingsTab extends SettingsTab with Logging { Padding2D(bottom = 4), ) - children :+= new PaddingBox(new Label("Generate new OpenComputers configuration file in:"), + children :+= new PaddingBox(new Label(m"label.settings.system.confGenerate"), Padding2D(top = 8, bottom = 8)) children :+= new Widget { children :+= new Button(new LabelTooltip(new File(OcelotPaths.openComputersConfigName).getCanonicalPath)) { - override def text: String = "Current Directory" + override def text = m"label.settings.system.currentDir" override def onClick(): Unit = { unpackConfig(new File(OcelotPaths.openComputersConfigName)) } } children :+= new PaddingBox( new Button(new LabelTooltip(OcelotPaths.openComputersConfig.toString)) { - override def text: String = "Config Directory" + override def text = m"label.settings.system.configDir" override def onClick(): Unit = { unpackConfig(OcelotPaths.openComputersConfig.toFile) } @@ -85,7 +86,7 @@ class SystemSettingsTab extends SettingsTab with Logging { ) children :+= new PaddingBox( new Button(new LabelTooltip("...")) { - override def text: String = "Custom Path" + override def text = m"label.settings.system.customDir" override def onClick(): Unit = { OcelotDesktop.showFileChooserDialog(JFileChooser.SAVE_DIALOG, JFileChooser.FILES_ONLY) { case Some(file) => @@ -100,7 +101,7 @@ class SystemSettingsTab extends SettingsTab with Logging { } children :+= new PaddingBox( - new Checkbox("Workspace autosave", Settings.get.autosave) { + new Checkbox(m"label.settings.system.autosave", Settings.get.autosave) { override def minimumSize: Size2D = Size2D(512, 24) override def onValueChanged(value: Boolean): Unit = { @@ -113,7 +114,7 @@ class SystemSettingsTab extends SettingsTab with Logging { children :+= new PaddingBox( new Widget { - children :+= new PaddingBox(new Label("Workspace autosave period (seconds):"), Padding2D(top = 4, right = 8)) + children :+= new PaddingBox(new Label(m"label.settings.system.autosavePeriod"), Padding2D(top = 4, right = 8)) children :+= new TextInput(Settings.get.autosavePeriod.toString) { override def onInput(text: String): Unit = { @@ -131,12 +132,12 @@ class SystemSettingsTab extends SettingsTab with Logging { private def unpackConfig(file: File): Unit = { if (file.exists()) { - new NotificationDialog(s"You are overriding an existing file!", NotificationType.Warning) { - addButton("Proceed") { + new NotificationDialog(m"error.ocelot.overriding", NotificationType.Warning) { + addButton(m"button.proceed") { unpackAndOverrideConfig(file) close() } - }.addCloseButton("Cancel").show() + }.addCloseButton(m"button.cancel").show() } else { unpackAndOverrideConfig(file) } @@ -150,7 +151,7 @@ class SystemSettingsTab extends SettingsTab with Logging { setConfigPath(dir.getCanonicalPath) case Failure(exception) => new NotificationDialog( - s"Something went wrong!\n($exception)\nCheck the log file for a full stacktrace.", + s"${m"error.ocelot.somethingWentWrong"}\n($exception)\n${m"error.ocelot.checkTheLogs"}", NotificationType.Error, ).addCloseButton().show() } @@ -167,7 +168,7 @@ class SystemSettingsTab extends SettingsTab with Logging { override def color: Color = ColorScheme("ErrorMessage") - override def text = "Restart Ocelot to apply new configuration" + override def text: Message = m"label.settings.system.restart" } children :+= new PaddingBox(restartWarning, Padding2D(top = 4, bottom = 8)) diff --git a/src/main/scala/ocelot/desktop/ui/widget/settings/UISettingsTab.scala b/src/main/scala/ocelot/desktop/ui/widget/settings/UISettingsTab.scala index e406c4f..29ca490 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/settings/UISettingsTab.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/settings/UISettingsTab.scala @@ -4,13 +4,15 @@ import ocelot.desktop.Settings import ocelot.desktop.geometry.{Padding2D, Size2D} import ocelot.desktop.graphics.IconSource import ocelot.desktop.ui.UiHandler -import ocelot.desktop.ui.layout.LinearLayout -import ocelot.desktop.ui.widget.{Checkbox, Label, PaddingBox, Slider, Widget} +import ocelot.desktop.ui.layout.{AlignItems, LinearLayout} +import ocelot.desktop.ui.widget.{Checkbox, Dropdown, Label, PaddingBox, Slider, Widget} import ocelot.desktop.util.MathUtils.roundAt +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Language, Message, Messages} class UISettingsTab extends SettingsTab { override val icon: IconSource = IconSource.SettingsUI - override val label: String = "UI" + override val label: Message = m"label.settings.ui" override def applySettings(): Unit = { if (UiHandler.fullScreen != Settings.get.windowFullscreen) { @@ -20,10 +22,35 @@ class UISettingsTab extends SettingsTab { if (UiHandler.scalingFactor != Settings.get.scaleFactor) { UiHandler.scalingFactor = Settings.get.scaleFactor } + + if (Messages.getCurrentLanguageCode != Settings.get.language) { + Messages.load(Settings.get.language) + } } + private val languages = Messages.loadLanguagesManifest() + private var selectedLanguage = languages.find(lang => lang.code == Settings.get.language).map(lang => lang.name).getOrElse("") + + children :+= new PaddingBox(new Widget { + override val layout = new LinearLayout(this, alignItems = AlignItems.Start) + + children :+= new PaddingBox(new Label(m"label.settings.ui.language") { + override def maximumSize: Size2D = minimumSize + }, Padding2D(top = 4, right = 8)) + + children :+= new Dropdown(languages.map(lang => lang.name)) { + override def text: Message = selectedLanguage + + override def onSelect(option: String): Unit = { + selectedLanguage = option + Settings.get.language = languages.find(lang => lang.name == option).map(lang => lang.code).getOrElse(Language.DefaultCode) + applySettings() + } + } + }, Padding2D(bottom = 8)) + children :+= new PaddingBox( - new Checkbox("Pin newly opened windows", + new Checkbox(m"label.settings.ui.pinWindows", initialValue = Settings.get.pinNewWindows) { override def minimumSize: Size2D = Size2D(512, 24) @@ -35,7 +62,7 @@ class UISettingsTab extends SettingsTab { ) children :+= new PaddingBox( - new Checkbox("Fullscreen mode", + new Checkbox(m"label.settings.ui.fullscreen", initialValue = Settings.get.windowFullscreen) { override def minimumSize: Size2D = Size2D(512, 24) @@ -48,7 +75,7 @@ class UISettingsTab extends SettingsTab { ) children :+= new PaddingBox( - new Checkbox("Save workspace on exit", + new Checkbox(m"label.settings.ui.saveOnExit", initialValue = Settings.get.saveOnExit) { override def minimumSize: Size2D = Size2D(512, 24) @@ -60,7 +87,7 @@ class UISettingsTab extends SettingsTab { ) children :+= new PaddingBox( - new Checkbox("Re-open last workspace on startup", + new Checkbox(m"label.settings.ui.openLastWorkspace", initialValue = Settings.get.openLastWorkspace) { override def minimumSize: Size2D = Size2D(512, 24) @@ -72,7 +99,7 @@ class UISettingsTab extends SettingsTab { ) children :+= new PaddingBox( - new Checkbox("Show previews on screen blocks", + new Checkbox(m"label.settings.ui.screenPreview", initialValue = Settings.get.renderScreenDataOnNodes) { override def minimumSize: Size2D = Size2D(512, 24) @@ -84,7 +111,7 @@ class UISettingsTab extends SettingsTab { ) children :+= new PaddingBox( - new Checkbox("Enable mipmaps for screen windows", + new Checkbox(m"label.settings.ui.mipmaps", initialValue = Settings.get.screenWindowMipmap) { override def minimumSize: Size2D = Size2D(512, 24) @@ -96,7 +123,7 @@ class UISettingsTab extends SettingsTab { ) children :+= new PaddingBox( - new Checkbox("Enable festive decorations", + new Checkbox(m"label.settings.ui.festiveDecorations", initialValue = Settings.get.enableFestiveDecorations) { override def minimumSize: Size2D = Size2D(512, 24) @@ -108,7 +135,7 @@ class UISettingsTab extends SettingsTab { ) children :+= new PaddingBox( - new Slider((Settings.get.scaleFactor - 1) / 2, "Interface scale", 5) { + new Slider((Settings.get.scaleFactor - 1) / 2, m"label.settings.ui.scale", 5) { override def minimumSize: Size2D = Size2D(512, 24) // Interpolates [0; 1] as [1; 3] rounded to nearest 0.5 @@ -124,13 +151,13 @@ class UISettingsTab extends SettingsTab { Padding2D(bottom = 8), ) - children :+= new PaddingBox(new Label("Tooltip delays:"), Padding2D(bottom = 8)) + children :+= new PaddingBox(new Label(m"label.settings.ui.tooltipDelay"), Padding2D(bottom = 8)) children :+= new PaddingBox( new Widget { override val layout = new LinearLayout(this) - children :+= new Slider(Settings.get.tooltipDelayItem / 2.0f, "Items") { + children :+= new Slider(Settings.get.tooltipDelayItem / 2.0f, m"label.settings.ui.tooltip.items") { override def minimumSize: Size2D = Size2D(247, 24) private def convert(value: Float): Float = roundAt(2)(value * 2.0f).toFloat @@ -146,7 +173,7 @@ class UISettingsTab extends SettingsTab { override def maximumSize: Size2D = Size2D(16, 8) } - children :+= new Slider(Settings.get.tooltipDelayUI / 2.0f, "UI Elements") { + children :+= new Slider(Settings.get.tooltipDelayUI / 2.0f, m"label.settings.ui.tooltip.ui") { override def minimumSize: Size2D = Size2D(247, 24) private def convert(value: Float): Float = roundAt(2)(value * 2.0f).toFloat diff --git a/src/main/scala/ocelot/desktop/ui/widget/statusbar/KeyEntry.scala b/src/main/scala/ocelot/desktop/ui/widget/statusbar/KeyEntry.scala index 701a0d8..5c6c873 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/statusbar/KeyEntry.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/statusbar/KeyEntry.scala @@ -5,10 +5,10 @@ import ocelot.desktop.color.Color import ocelot.desktop.geometry.Size2D import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.widget.Widget -import ocelot.desktop.util.DrawUtils +import ocelot.desktop.util.{DrawUtils, Message} -class KeyEntry(val key: String, val text: String) extends Widget { - override def minimumSize: Size2D = Size2D(key.length * 8 + 24 + text.length * 8, 16) +class KeyEntry(val key: String, val text: Message) extends Widget { + override def minimumSize: Size2D = Size2D(key.length * 8 + 24 + text.toString.length * 8, 16) override def maximumSize: Size2D = minimumSize override def draw(g: Graphics): Unit = { @@ -19,6 +19,6 @@ class KeyEntry(val key: String, val text: String) extends Widget { g.foreground = ColorScheme("StatusBarForeground") g.setNormalFont() - g.text(position.x + key.length * 8 + 16, position.y, text) + g.text(position.x + key.length * 8 + 16, position.y, text.toString) } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/statusbar/KeyMouseEntry.scala b/src/main/scala/ocelot/desktop/ui/widget/statusbar/KeyMouseEntry.scala index 02fe05c..7ef5e14 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/statusbar/KeyMouseEntry.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/statusbar/KeyMouseEntry.scala @@ -5,12 +5,12 @@ import ocelot.desktop.color.Color import ocelot.desktop.geometry.Size2D import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.widget.Widget -import ocelot.desktop.util.{DrawUtils, Spritesheet} +import ocelot.desktop.util.{DrawUtils, Message, Spritesheet} -class KeyMouseEntry(val icon: String, val key: String, val text: String) extends Widget { +class KeyMouseEntry(val icon: String, val key: String, val text: Message) extends Widget { private val iconSize = Spritesheet.spriteSize(icon) - override def minimumSize: Size2D = Size2D(iconSize.width + key.length * 8 + 40 + text.length * 8, 16) + override def minimumSize: Size2D = Size2D(iconSize.width + key.length * 8 + 40 + text.toString.length * 8, 16) override def maximumSize: Size2D = minimumSize override def draw(g: Graphics): Unit = { @@ -23,6 +23,6 @@ class KeyMouseEntry(val icon: String, val key: String, val text: String) extends g.foreground = ColorScheme("StatusBarForeground") g.setNormalFont() - g.text(position.x + iconSize.width + key.length * 8 + 24, position.y, text) + g.text(position.x + iconSize.width + key.length * 8 + 24, position.y, text.toString) } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/statusbar/MouseEntry.scala b/src/main/scala/ocelot/desktop/ui/widget/statusbar/MouseEntry.scala index af9fd3c..c9e9656 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/statusbar/MouseEntry.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/statusbar/MouseEntry.scala @@ -5,18 +5,18 @@ import ocelot.desktop.color.Color import ocelot.desktop.geometry.Size2D import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.widget.Widget -import ocelot.desktop.util.Spritesheet +import ocelot.desktop.util.{Message, Spritesheet} -class MouseEntry(val icon: String, val text: String) extends Widget { +class MouseEntry(val icon: String, val text: Message) extends Widget { private val iconSize = Spritesheet.spriteSize(icon) - override def minimumSize: Size2D = Size2D(iconSize.width + 24 + text.length * 8, 16) + override def minimumSize: Size2D = Size2D(iconSize.width + 24 + text.toString.length * 8, 16) override def maximumSize: Size2D = minimumSize override def draw(g: Graphics): Unit = { g.sprite(icon, position + Size2D(8, height / 2 - iconSize.height / 2), ColorScheme("StatusBarIcon")) g.background = Color.Transparent g.foreground = ColorScheme("StatusBarForeground") - g.text(position.x + iconSize.width + 16, position.y, text) + g.text(position.x + iconSize.width + 16, position.y, text.toString) } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/statusbar/StatusBar.scala b/src/main/scala/ocelot/desktop/ui/widget/statusbar/StatusBar.scala index e1f13ca..85bab34 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/statusbar/StatusBar.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/statusbar/StatusBar.scala @@ -10,6 +10,8 @@ import ocelot.desktop.ui.layout.{AlignItems, LinearLayout} import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.traits.HoverAnimation +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.{ColorScheme, OcelotDesktop} import org.lwjgl.input.Keyboard @@ -35,7 +37,7 @@ class StatusBar extends Widget { new Label { override def maximumSize: Size2D = minimumSize override def color: Color = ColorScheme("StatusBarFPS") - override def text: String = f"FPS: ${UiHandler.fps}%02.1f" + override def text = f"FPS: ${UiHandler.fps}%02.1f" }, Padding2D(left = 8, right = 8), ) @@ -65,13 +67,13 @@ class StatusBar extends Widget { case ClickEvent(MouseEvent.Button.Right, pos) => val menu = new ContextMenu menu.addEntry( - ContextMenuEntry("Change simulation speed", IconSource.Edit) { + ContextMenuEntry(m"label.menu.changeSimulationSpeed", IconSource.Edit) { new ChangeSimulationSpeedDialog().show() } ) menu.addEntry( - ContextMenuEntry("Reset simulation speed", IconSource.Restart) { + ContextMenuEntry(m"label.menu.resetSimulationSpeed", IconSource.Restart) { OcelotDesktop.ticker.tickInterval = 50.millis } ) @@ -100,7 +102,7 @@ class StatusBar extends Widget { super.update() if (isHovered) { - root.get.statusBar.addMouseEntry("icons/RMB", "Change simulation speed") + root.get.statusBar.addMouseEntry("icons/RMB", m"label.menu.changeSimulationSpeed") } } @@ -108,7 +110,7 @@ class StatusBar extends Widget { override def color: Color = ColorScheme("StatusBarTPS") - override def text: String = f"TPS: ${OcelotDesktop.tpsCounter.fps}%02.1f" + override def text = f"TPS: ${OcelotDesktop.tpsCounter.fps}%02.1f" } object HoverBox extends PaddingBox(TpsLabel, Padding2D(left = 8, right = 8)) with HoverAnimation { @@ -133,7 +135,7 @@ class StatusBar extends Widget { Padding2D(left = 8), ) - def addMouseEntry(icon: String, text: String): Unit = { + def addMouseEntry(icon: String, text: Message): Unit = { if ( !keyMouseEntries.children.filter(_.isInstanceOf[MouseEntry]).map(_.asInstanceOf[MouseEntry]).exists( _.icon == icon @@ -142,7 +144,7 @@ class StatusBar extends Widget { keyMouseEntries.children :+= new MouseEntry(icon, text) } - def addKeyMouseEntry(icon: String, key: String, text: String): Unit = { + def addKeyMouseEntry(icon: String, key: String, text: Message): Unit = { if ( !keyMouseEntries.children.filter(_.isInstanceOf[KeyMouseEntry]).map(_.asInstanceOf[KeyMouseEntry]).exists(v => v.icon == icon && v.key == key @@ -151,7 +153,7 @@ class StatusBar extends Widget { keyMouseEntries.children :+= new KeyMouseEntry(icon, key, text) } - def addKeyEntry(key: String, text: String): Unit = { + def addKeyEntry(key: String, text: Message): Unit = { if (!keyEntries.children.map(_.asInstanceOf[KeyEntry]).exists(_.key == key)) keyEntries.children :+= new KeyEntry(key, text) } diff --git a/src/main/scala/ocelot/desktop/ui/widget/tooltip/ItemTooltip.scala b/src/main/scala/ocelot/desktop/ui/widget/tooltip/ItemTooltip.scala index 5018475..7ef7c13 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/tooltip/ItemTooltip.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/tooltip/ItemTooltip.scala @@ -4,15 +4,16 @@ import ocelot.desktop.Settings import ocelot.desktop.color.Color import ocelot.desktop.geometry.Padding2D import ocelot.desktop.ui.widget.Label +import ocelot.desktop.util.Message class ItemTooltip extends Tooltip { override val DelayTime: Float = Settings.get.tooltipDelayItem override def bodyPadding: Padding2D = Padding2D.equal(5) - def addLine(_text: String, _color: Color = Color.Grey): Unit = { + def addLine(_text: Message, _color: Color = Color.Grey): Unit = { body.children :+= new Label { - override def text: String = _text + override def text: Message = _text override def color: Color = _color } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/tooltip/LabelTooltip.scala b/src/main/scala/ocelot/desktop/ui/widget/tooltip/LabelTooltip.scala index 04e18c7..72e3311 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/tooltip/LabelTooltip.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/tooltip/LabelTooltip.scala @@ -4,13 +4,16 @@ import ocelot.desktop.Settings import ocelot.desktop.color.Color import ocelot.desktop.geometry.Padding2D import ocelot.desktop.ui.widget.Label +import ocelot.desktop.util.Message class LabelTooltip(text: String) extends Tooltip { override val DelayTime: Float = Settings.get.tooltipDelayUI + def this(text: Message) = this(text.toString) + for (line <- text.split("\n")) { body.children :+= new Label { - override def text: String = line + override def text: Message = line override def color: Color = Color.White } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/verticalmenu/VerticalMenuButton.scala b/src/main/scala/ocelot/desktop/ui/widget/verticalmenu/VerticalMenuButton.scala index c332135..019d25b 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/verticalmenu/VerticalMenuButton.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/verticalmenu/VerticalMenuButton.scala @@ -9,10 +9,10 @@ import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler} import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent} import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget._ -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.{Message, Orientation} import ocelot.desktop.util.animation.ColorAnimation -class VerticalMenuButton(icon: IconSource, label: String, handler: VerticalMenuButton => Unit = _ => {}) +class VerticalMenuButton(icon: IconSource, label: Message, handler: VerticalMenuButton => Unit = _ => {}) extends Widget with MouseHandler with HoverHandler { val colorAnimation: ColorAnimation = new ColorAnimation(ColorScheme("VerticalMenuBackground"), 0.6f) @@ -29,7 +29,7 @@ class VerticalMenuButton(icon: IconSource, label: String, handler: VerticalMenuB ) children :+= new Label { - override def text: String = label + override def text: Message = label override def color: Color = ColorScheme("VerticalMenuEntryForeground") override def maximumSize: Size2D = Size2D(Float.PositiveInfinity, 16) } diff --git a/src/main/scala/ocelot/desktop/ui/widget/window/NodeSelector.scala b/src/main/scala/ocelot/desktop/ui/widget/window/NodeSelector.scala index 12f87c0..ead4ebd 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/window/NodeSelector.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/window/NodeSelector.scala @@ -10,7 +10,7 @@ import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.layout.{AlignItems, JustifyContent, LinearLayout} import ocelot.desktop.ui.widget._ import ocelot.desktop.util.animation.{ColorAnimation, UnitAnimation} -import ocelot.desktop.util.{DefaultSlotItemsFillable, DrawUtils, Logging, Orientation} +import ocelot.desktop.util.{DefaultSlotItemsFillable, DrawUtils, Logging, Message, Orientation} import scala.collection.immutable.ArraySeq @@ -68,7 +68,7 @@ class NodeSelector extends Window with Logging { new LinearLayout(this, Orientation.Vertical, JustifyContent.Center, AlignItems.Center) children :+= new Label { - override def text: String = group.name + override def text: Message = group.name override def color: Color = ColorScheme("NodeSelectorRing") } diff --git a/src/main/scala/ocelot/desktop/ui/widget/window/PanelWindow.scala b/src/main/scala/ocelot/desktop/ui/widget/window/PanelWindow.scala index 1148190..0417782 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/window/PanelWindow.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/window/PanelWindow.scala @@ -3,14 +3,14 @@ package ocelot.desktop.ui.widget.window import ocelot.desktop.geometry.Padding2D import ocelot.desktop.ui.layout.{Layout, LinearLayout} import ocelot.desktop.ui.widget.{PaddingBox, Widget} -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.{Message, Orientation} import scala.collection.immutable.ArraySeq trait PanelWindow extends BasicWindow { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) - protected def title: String + protected def title: Message protected def titleMaxLength: Int = 32 @@ -19,7 +19,7 @@ trait PanelWindow extends BasicWindow { children :+= new PaddingBox( new TitleBar(PanelWindow.this) { - override def title: String = PanelWindow.this.title + override def title: Message = PanelWindow.this.title override def titleMaxLength: Int = PanelWindow.this.titleMaxLength }, Padding2D(top = 8, left = 12, right = 12, bottom = 2), diff --git a/src/main/scala/ocelot/desktop/ui/widget/window/ProfilerWindow.scala b/src/main/scala/ocelot/desktop/ui/widget/window/ProfilerWindow.scala index 755453a..e207ede 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/window/ProfilerWindow.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/window/ProfilerWindow.scala @@ -3,12 +3,13 @@ package ocelot.desktop.ui.widget.window import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.widget.{Label, Widget} -import ocelot.desktop.util.{DrawUtils, Orientation, Profiler} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{DrawUtils, Message, Orientation, Profiler} import scala.collection.immutable.ArraySeq class ProfilerWindow extends PanelWindow { - override protected def title: String = "Profiler" + override protected def title: Message = m"title.profiler" private val inner: Widget = new Widget { override val layout = new LinearLayout(this, orientation = Orientation.Vertical) @@ -20,7 +21,7 @@ class ProfilerWindow extends PanelWindow { inner.children = ArraySeq.empty for (line <- Profiler.report()) { inner.children :+= new Label { - override def text: String = line + override def text: Message = line } } diff --git a/src/main/scala/ocelot/desktop/ui/widget/window/TitleBar.scala b/src/main/scala/ocelot/desktop/ui/widget/window/TitleBar.scala index b1710c1..45e316b 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/window/TitleBar.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/window/TitleBar.scala @@ -2,10 +2,10 @@ package ocelot.desktop.ui.widget.window import ocelot.desktop.ui.layout.{AlignItems, Layout, LinearLayout} import ocelot.desktop.ui.widget.{IconButton, Label, Widget} -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.{Message, Orientation} class TitleBar(val window: Window) extends Widget { - def title: String = "Hello world" + def title: Message = "Hello world" def titleMaxLength: Int = 32 def isPinned: Boolean = window.isPinned def onPinRequested(): Unit = window.pin() @@ -16,7 +16,7 @@ class TitleBar(val window: Window) extends Widget { orientation = Orientation.Horizontal, alignItems = AlignItems.Center) children :+= new Label { - override def text: String = { + override def text: Message = { if (title.length > titleMaxLength) title.take(titleMaxLength - 1) + "…" else diff --git a/src/main/scala/ocelot/desktop/ui/widget/window/Window.scala b/src/main/scala/ocelot/desktop/ui/widget/window/Window.scala index f14f5f1..21be65b 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/window/Window.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/window/Window.scala @@ -6,6 +6,7 @@ import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.event.handlers.MouseHandler import ocelot.desktop.ui.event.{DragEvent, KeyEvent, MouseEvent} import ocelot.desktop.ui.widget.Widget +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.Persistable import org.lwjgl.input.Keyboard import totoro.ocelot.brain.nbt.NBTTagCompound @@ -244,6 +245,6 @@ trait Window extends Widget with Persistable with MouseHandler { super.update() if (canCloseOnEsc) - root.get.statusBar.addKeyEntry("ESC", "Close window") + root.get.statusBar.addKeyEntry("ESC", m"label.bar.closeWindow") } } diff --git a/src/main/scala/ocelot/desktop/util/ComputerAware.scala b/src/main/scala/ocelot/desktop/util/ComputerAware.scala index 95a496a..1233c27 100644 --- a/src/main/scala/ocelot/desktop/util/ComputerAware.scala +++ b/src/main/scala/ocelot/desktop/util/ComputerAware.scala @@ -8,6 +8,7 @@ import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, Cont import ocelot.desktop.ui.widget.slot._ import ocelot.desktop.ui.widget.window.Windowed import ocelot.desktop.util.ComputerType.ComputerType +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.windows.ComputerWindow import totoro.ocelot.brain.entity.traits.{Computer, TieredPersistable} import totoro.ocelot.brain.loot.Loot @@ -42,16 +43,16 @@ trait ComputerAware extends Logging with SyncedInventory with DefaultSlotItemsFi def addPowerContextMenuEntries(menu: ContextMenu): Unit = { if (computer.machine.isRunning) { - menu.addEntry(ContextMenuEntry("Turn off", IconSource.Power) { + menu.addEntry(ContextMenuEntry(m"label.menu.turnOff", IconSource.Power) { turnOff() }) - menu.addEntry(ContextMenuEntry("Reboot", IconSource.Restart) { + menu.addEntry(ContextMenuEntry(m"label.menu.reboot", IconSource.Restart) { turnOff() turnOn() }) } else { - menu.addEntry(ContextMenuEntry("Turn on", IconSource.Power) { + menu.addEntry(ContextMenuEntry(m"label.menu.turnOn", IconSource.Power) { turnOn() }) } @@ -59,7 +60,7 @@ trait ComputerAware extends Logging with SyncedInventory with DefaultSlotItemsFi def addTierContextMenuEntries(menu: ContextMenu, maxNonCreativeTier: Tier = Tier.Three): Unit = { menu.addEntry( - new ContextMenuSubmenu("Change tier", Some(ContextMenuIcon(IconSource.Tiers))) { + new ContextMenuSubmenu(m"label.menu.changeTier", Some(ContextMenuIcon(IconSource.Tiers))) { def addTierEntry(tier: Tier): Unit = { addEntry(ContextMenuEntry(tier.label) { changeTier(tier) diff --git a/src/main/scala/ocelot/desktop/util/DiskDriveAware.scala b/src/main/scala/ocelot/desktop/util/DiskDriveAware.scala index cea3914..30422b2 100644 --- a/src/main/scala/ocelot/desktop/util/DiskDriveAware.scala +++ b/src/main/scala/ocelot/desktop/util/DiskDriveAware.scala @@ -11,6 +11,7 @@ import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.slot._ import ocelot.desktop.ui.widget.window.Windowed import ocelot.desktop.util.DiskDriveAware.ColorValues +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.windows.DiskDriveWindow import totoro.ocelot.brain.entity.FloppyDiskDrive import totoro.ocelot.brain.entity.traits.Inventory @@ -61,7 +62,7 @@ trait DiskDriveAware extends Logging with SyncedInventory with DefaultSlotItemsF } def addEjectContextMenuEntry(menu: ContextMenu): Unit = { - menu.addEntry(ContextMenuEntry("Eject", IconSource.Eject) { + menu.addEntry(ContextMenuEntry(m"label.menu.eject", IconSource.Eject) { eject() }) } @@ -78,23 +79,23 @@ trait DiskDriveAware extends Logging with SyncedInventory with DefaultSlotItemsF } object DiskDriveAware { - val ColorNames: Map[DyeColor, String] = Map( - DyeColor.White -> "White", - DyeColor.Orange -> "Orange", - DyeColor.Magenta -> "Magenta", - DyeColor.LightBlue -> "LightBlue", - DyeColor.Yellow -> "Yellow", - DyeColor.Lime -> "Lime", - DyeColor.Pink -> "Pink", - DyeColor.Gray -> "Gray", - DyeColor.Silver -> "Silver", - DyeColor.Cyan -> "Cyan", - DyeColor.Purple -> "Purple", - DyeColor.Blue -> "Blue", - DyeColor.Brown -> "Brown", - DyeColor.Green -> "Green", - DyeColor.Red -> "Red", - DyeColor.Black -> "Black", + val ColorNames: Map[DyeColor, Message] = Map( + DyeColor.White -> m"color.white", + DyeColor.Orange -> m"color.orange", + DyeColor.Magenta -> m"color.magenta", + DyeColor.LightBlue -> m"color.lightBlue", + DyeColor.Yellow -> m"color.yellow", + DyeColor.Lime -> m"color.lime", + DyeColor.Pink -> m"color.pink", + DyeColor.Gray -> m"color.gray", + DyeColor.Silver -> m"color.silver", + DyeColor.Cyan -> m"color.cyan", + DyeColor.Purple -> m"color.purple", + DyeColor.Blue -> m"color.blue", + DyeColor.Brown -> m"color.brown", + DyeColor.Green -> m"color.green", + DyeColor.Red -> m"color.red", + DyeColor.Black -> m"color.black", ) val ColorValues: Map[DyeColor, Int] = Map( diff --git a/src/main/scala/ocelot/desktop/util/Language.scala b/src/main/scala/ocelot/desktop/util/Language.scala new file mode 100644 index 0000000..a871593 --- /dev/null +++ b/src/main/scala/ocelot/desktop/util/Language.scala @@ -0,0 +1,9 @@ +package ocelot.desktop.util + +case class Language(code: String, name: String) + +object Language { + val DefaultCode = "en_US" + + def apply(varargs: Array[String]): Language = Language(varargs(0), varargs(1)) +} diff --git a/src/main/scala/ocelot/desktop/util/Message.scala b/src/main/scala/ocelot/desktop/util/Message.scala new file mode 100644 index 0000000..f885fa1 --- /dev/null +++ b/src/main/scala/ocelot/desktop/util/Message.scala @@ -0,0 +1,53 @@ +package ocelot.desktop.util + +import scala.language.implicitConversions + +/** + * Holds potentially localized string literal. + * Resolves that literal on a `toString` call. + * @param value string value + * @param localized if `true`, the value above is treated as a key to a translated message in a lang file + * if `false` the value is used as-is + */ + +case class Message(value: String, localized: Boolean = true) { + override def toString: String = { + if (localized) { + Messages.lift(value) match { + case Some(value) => value.replace("[nl]", "\n") + case None => value + } + } else value + } + + def toStringMultiline: Seq[String] = { + if (localized) { + Messages.lift(value) match { + case Some(value) => value.split("\\[nl]") + case None => value.split('\n') + } + } else value.split('\n') + } + + def length: Int = toString.length + + /* + * Any modifications to the string value of a Message force it to be resolved. + * Thus, if the interface will dynamically update its language, these modifications will have to be repeated. + * On practice that usually means, that any UI widgets that use the modified Message will have to be re-created, + * in order to reflect the language change. + */ + def take(n: Int): Message = Message(toString.take(n), localized = false) + + def +(string: String): Message = Message(toString + string, localized = false) +} + +object Message { + implicit class LocalizedMessage(val sc: StringContext) extends AnyVal { + def m(args: Any*): Message = { + Message(sc.s(args: _*)) + } + } + + implicit def stringToMessage(s: String): Message = Message(s, localized = false) +} diff --git a/src/main/scala/ocelot/desktop/util/Messages.scala b/src/main/scala/ocelot/desktop/util/Messages.scala index 9075145..62c2aa5 100644 --- a/src/main/scala/ocelot/desktop/util/Messages.scala +++ b/src/main/scala/ocelot/desktop/util/Messages.scala @@ -5,19 +5,27 @@ import scala.io.Source object Messages extends Logging with PartialFunction[String, String] { private val entries = new mutable.HashMap[String, String]() + private var currentLangCode = Language.DefaultCode override def apply(key: String): String = entries(key) override def isDefinedAt(key: String): Boolean = entries.isDefinedAt(key) - def load(source: Source): Unit = { - logger.info(s"Loading messages") + def load(langCode: String): Unit = { + logger.info(s"Loading localized messages for '$langCode'...") - val oldSize = entries.size + var source = langFileSource(langCode) + if (source == null) { + logger.error(s"Cannot find localized messages for '$langCode'! Loading defaults (${Language.DefaultCode})...") + source = langFileSource(Language.DefaultCode) + currentLangCode = Language.DefaultCode + } else { + currentLangCode = langCode + } for (line_ <- source.getLines) { val line = line_.trim - if (line.nonEmpty) { + if (line.nonEmpty && !line.startsWith("#")) { val split = line.split("\\s*=\\s*") val key = split(0) val value = split(1) @@ -25,6 +33,17 @@ object Messages extends Logging with PartialFunction[String, String] { } } - logger.info(s"Loaded ${entries.size - oldSize} messages") + logger.info(s"Loaded ${entries.size} messages") } + + private def langFileSource(langCode: String): Source = { + Source.fromURL(getClass.getResource(s"/ocelot/desktop/lang/$langCode.lang")) + } + + def loadLanguagesManifest(): List[Language] = { + val source = Source.fromURL(getClass.getResource(s"/ocelot/desktop/lang/manifest")) + source.getLines.map(line => Language(line.trim.split("\\s*=\\s*"))).toList + } + + def getCurrentLanguageCode: String = currentLangCode } diff --git a/src/main/scala/ocelot/desktop/util/OcelotInterfaceLogStorage.scala b/src/main/scala/ocelot/desktop/util/OcelotInterfaceLogStorage.scala index 8d8405b..d299db7 100644 --- a/src/main/scala/ocelot/desktop/util/OcelotInterfaceLogStorage.scala +++ b/src/main/scala/ocelot/desktop/util/OcelotInterfaceLogStorage.scala @@ -16,7 +16,7 @@ trait OcelotInterfaceLogStorage extends EventAware with Persistable with Windowe def ocelotInterface: OcelotInterface - def name: String + def name: Message override def createWindow(): OcelotInterfaceWindow = new OcelotInterfaceWindow(this) diff --git a/src/main/scala/ocelot/desktop/util/SettingsData.scala b/src/main/scala/ocelot/desktop/util/SettingsData.scala index a277660..478588b 100644 --- a/src/main/scala/ocelot/desktop/util/SettingsData.scala +++ b/src/main/scala/ocelot/desktop/util/SettingsData.scala @@ -35,6 +35,8 @@ class SettingsData { var recentWorkspace: Option[String] = None + var language: String = Language.DefaultCode + var pinNewWindows: Boolean = true var unfocusedWindowTransparency: Double = 0.5 var saveOnExit: Boolean = true diff --git a/src/main/scala/ocelot/desktop/util/Spritesheet.scala b/src/main/scala/ocelot/desktop/util/Spritesheet.scala index 3e4cf81..4a65d26 100644 --- a/src/main/scala/ocelot/desktop/util/Spritesheet.scala +++ b/src/main/scala/ocelot/desktop/util/Spritesheet.scala @@ -30,7 +30,7 @@ object Spritesheet extends Resource with Logging { } def load(): Unit = { - logger.info("Loading sprites") + logger.info("Loading sprites...") val imageURL = getClass.getResource("/ocelot/desktop/images/spritesheet/spritesheet.png") val image = ImageIO.read(imageURL) diff --git a/src/main/scala/ocelot/desktop/windows/CameraWindow.scala b/src/main/scala/ocelot/desktop/windows/CameraWindow.scala index 9ab9764..a361c0c 100644 --- a/src/main/scala/ocelot/desktop/windows/CameraWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/CameraWindow.scala @@ -8,14 +8,15 @@ import ocelot.desktop.ui.layout.{Layout, LinearLayout} import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.window.PanelWindow -import ocelot.desktop.util.{Orientation, WebcamCapture} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation, WebcamCapture} import scala.jdk.CollectionConverters.CollectionHasAsScala class CameraWindow(cameraNode: CameraNode) extends PanelWindow { def camera: Camera = cameraNode.camera - override protected def title: String = s"Camera ${cameraNode.label.get}" + override protected def title: Message = s"${m"component.camera"} ${cameraNode.label.get}" override protected def titleMaxLength: Int = 36 setInner(new Widget { @@ -25,7 +26,7 @@ class CameraWindow(cameraNode: CameraNode) extends PanelWindow { new Button { override def minimumSize: Size2D = super.minimumSize.copy(width = 256) override def maximumSize: Size2D = super.maximumSize.copy(width = 512) - override def text: String = camera.webcamCapture.map(_.name).getOrElse("") + override def text: Message = camera.webcamCapture.map(it => Message(it.name)).getOrElse(m"component.camera.notAvailable") override def onClick(): Unit = { val menu = new ContextMenu @@ -42,7 +43,7 @@ class CameraWindow(cameraNode: CameraNode) extends PanelWindow { ) children :+= new PaddingBox( - new Checkbox("Flip image horizontally", + new Checkbox(m"component.camera.flipHorizontally", initialValue = camera.flipHorizontally) { override def onValueChanged(newValue: Boolean): Unit = camera.flipHorizontally = newValue }, @@ -50,7 +51,7 @@ class CameraWindow(cameraNode: CameraNode) extends PanelWindow { ) children :+= new PaddingBox( - new Checkbox("Flip image vertically", + new Checkbox(m"component.camera.flipVertically", initialValue = camera.flipVertically) { override def onValueChanged(newValue: Boolean): Unit = camera.flipVertically = newValue }, @@ -58,7 +59,7 @@ class CameraWindow(cameraNode: CameraNode) extends PanelWindow { ) children :+= new PaddingBox( - new Checkbox("Unlimit call budget", + new Checkbox(m"component.camera.unlimitedCallBudget", initialValue = camera.directCalls) { override def onValueChanged(newValue: Boolean): Unit = camera.directCalls = newValue }, diff --git a/src/main/scala/ocelot/desktop/windows/ChestWindow.scala b/src/main/scala/ocelot/desktop/windows/ChestWindow.scala index a85d0c9..8344dbe 100644 --- a/src/main/scala/ocelot/desktop/windows/ChestWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/ChestWindow.scala @@ -7,7 +7,8 @@ import ocelot.desktop.ui.layout.{Layout, LinearLayout} import ocelot.desktop.ui.widget.Widget import ocelot.desktop.ui.widget.slot.SlotWidget import ocelot.desktop.ui.widget.window.PanelWindow -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} import ocelot.desktop.windows.ChestWindow.{Columns, Rows} import scala.collection.mutable.ArrayBuffer @@ -32,7 +33,7 @@ class ChestWindow(node: ChestNode) extends PanelWindow { } }) - override protected def title: String = "Chest " + node.label.getOrElse("") + override protected def title: Message = m"component.chest" + " " + node.label.getOrElse("") override def titleMaxLength: Int = 36 diff --git a/src/main/scala/ocelot/desktop/windows/ComputerWindow.scala b/src/main/scala/ocelot/desktop/windows/ComputerWindow.scala index baacfb6..efd62ae 100644 --- a/src/main/scala/ocelot/desktop/windows/ComputerWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/ComputerWindow.scala @@ -8,8 +8,9 @@ import ocelot.desktop.ui.layout.{Layout, LinearLayout} import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.traits.HoverHighlight import ocelot.desktop.ui.widget.window.{BasicWindow, TitleBar} +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.animation.UnitAnimation -import ocelot.desktop.util.{ComputerAware, ComputerType, DrawUtils, Orientation} +import ocelot.desktop.util.{ComputerAware, ComputerType, DrawUtils, Message, Orientation} import ocelot.desktop.{ColorScheme, OcelotDesktop} import totoro.ocelot.brain.nbt.NBTTagCompound @@ -28,7 +29,7 @@ class ComputerWindow(computerAware: ComputerAware) extends BasicWindow { "buttons/BottomDrawerClose", mode = IconButton.Mode.Switch, darkenActiveColorFactor = 0.2f, - tooltip = Some("Toggle computer usage histogram"), + tooltip = Some(m"label.window.computer.toggleStats"), ) { override def onPressed(): Unit = bottomDrawerAnimation.goUp() override def onReleased(): Unit = bottomDrawerAnimation.goDown() @@ -90,7 +91,7 @@ class ComputerWindow(computerAware: ComputerAware) extends BasicWindow { } children :+= new PerTickHistogram { - title = "Memory usage" + title = m"label.window.computer.memoryUsage" override def tickUpdate(): Unit = { super.tickUpdate() @@ -106,7 +107,7 @@ class ComputerWindow(computerAware: ComputerAware) extends BasicWindow { children :+= new PaddingBox( new PerTickHistogram { - title = "CPU usage" + title = m"label.window.computer.cpuUsage" override def tickUpdate(): Unit = { super.tickUpdate() @@ -123,7 +124,7 @@ class ComputerWindow(computerAware: ComputerAware) extends BasicWindow { children :+= new PaddingBox( new PerTickHistogram { - title = "Call budget usage" + title = m"label.window.computer.callBudget" override def tickUpdate(): Unit = { super.tickUpdate() @@ -168,11 +169,11 @@ class ComputerWindow(computerAware: ComputerAware) extends BasicWindow { // Title bar children :+= new PaddingBox( new TitleBar(ComputerWindow.this) { - override def title: String = { + override def title: Message = { val name = computerAware.computerType match { - case ComputerType.Server => "Server" - case ComputerType.Microcontroller => "Microcontroller" - case _ => "Computer" + case ComputerType.Server => m"label.window.server" + case ComputerType.Microcontroller => m"label.window.microcontroller" + case _ => m"label.window.computer" } s"$name ${computerAware.computer.machine.node.address}" diff --git a/src/main/scala/ocelot/desktop/windows/DiskDriveWindow.scala b/src/main/scala/ocelot/desktop/windows/DiskDriveWindow.scala index 86b5222..e944887 100644 --- a/src/main/scala/ocelot/desktop/windows/DiskDriveWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/DiskDriveWindow.scala @@ -4,12 +4,13 @@ import ocelot.desktop.geometry.Padding2D import ocelot.desktop.node.nodes.DiskDriveNode import ocelot.desktop.ui.widget.PaddingBox import ocelot.desktop.ui.widget.window.PanelWindow -import ocelot.desktop.util.DiskDriveAware +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{DiskDriveAware, Message} class DiskDriveWindow(diskDriveHost: DiskDriveAware) extends PanelWindow { setInner(new PaddingBox(diskDriveHost.floppySlotWidget, Padding2D(8, 64, 8, 64))) - override protected def title: String = "Disk Drive " + (diskDriveHost match { + override protected def title: Message = s"${m"component.diskDrive"} " + (diskDriveHost match { case diskDriveNode: DiskDriveNode => diskDriveNode.label case _ => None }).getOrElse("") diff --git a/src/main/scala/ocelot/desktop/windows/HologramProjectorWindow.scala b/src/main/scala/ocelot/desktop/windows/HologramProjectorWindow.scala index 9699996..3064b1d 100644 --- a/src/main/scala/ocelot/desktop/windows/HologramProjectorWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/HologramProjectorWindow.scala @@ -9,6 +9,8 @@ import ocelot.desktop.graphics.scene.{Scene3D, SceneMesh3D} import ocelot.desktop.node.nodes.HologramProjectorNode import ocelot.desktop.ui.widget.window.PanelWindow import ocelot.desktop.ui.widget.{IconButton, Viewport3DWidget, Widget} +import ocelot.desktop.util.Message +import ocelot.desktop.util.Message.LocalizedMessage import totoro.ocelot.brain.Settings import totoro.ocelot.brain.entity.HologramProjector import totoro.ocelot.brain.util.Tier @@ -223,7 +225,7 @@ class HologramProjectorWindow(val hologramProjectorNode: HologramProjectorNode) setInner(viewport) - override protected def title: String = "Hologram Projector " + hologramProjectorNode.label.getOrElse("") + override protected def title: Message = s"${m"component.hologramProjector"} " + hologramProjectorNode.label.getOrElse("") override protected def titleMaxLength: Int = 69 diff --git a/src/main/scala/ocelot/desktop/windows/OcelotInterfaceWindow.scala b/src/main/scala/ocelot/desktop/windows/OcelotInterfaceWindow.scala index 39aa839..050cef5 100644 --- a/src/main/scala/ocelot/desktop/windows/OcelotInterfaceWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/OcelotInterfaceWindow.scala @@ -5,10 +5,11 @@ import ocelot.desktop.ui.layout.{AlignItems, Layout, LinearLayout} import ocelot.desktop.ui.widget.LogWidget.LogEntry import ocelot.desktop.ui.widget.window.PanelWindow import ocelot.desktop.ui.widget.{Button, Checkbox, Filler, Label, LogWidget, PaddingBox, TextInput, Widget} -import ocelot.desktop.util.{OcelotInterfaceLogStorage, Orientation} +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, OcelotInterfaceLogStorage, Orientation} class OcelotInterfaceWindow(storage: OcelotInterfaceLogStorage) extends PanelWindow { - override protected def title: String = s"${storage.name} ${storage.ocelotInterface.node.address}" + override protected def title: Message = s"${storage.name} ${storage.ocelotInterface.node.address}" private val logWidget: LogWidget = new LogWidget { override protected def textWidth: Int = 50 @@ -29,7 +30,7 @@ class OcelotInterfaceWindow(storage: OcelotInterfaceLogStorage) extends PanelWin } children :+= new Label { - override def text: String = s"Messages: ${storage.entryCount} / ${storage.messageLimit}" + override def text: Message = s"${m"label.window.ocelot.messages"}: ${storage.entryCount} / ${storage.messageLimit}" } children :+= new Widget { @@ -38,7 +39,7 @@ class OcelotInterfaceWindow(storage: OcelotInterfaceLogStorage) extends PanelWin children :+= new PaddingBox( new Label { - override def text: String = "Limit" + override def text: Message = m"label.window.ocelot.limit" override def maximumSize: Size2D = minimumSize }, @@ -65,7 +66,7 @@ class OcelotInterfaceWindow(storage: OcelotInterfaceLogStorage) extends PanelWin children :+= new Filler - children :+= new Checkbox("Scroll to end") { + children :+= new Checkbox(m"label.window.ocelot.scrollToEnd") { override def checked: Boolean = logWidget.scrollToEnd override def checked_=(value: Boolean): Unit = { @@ -74,7 +75,7 @@ class OcelotInterfaceWindow(storage: OcelotInterfaceLogStorage) extends PanelWin } children :+= new Button { - override def text: String = "Clear" + override def text: Message = m"label.window.ocelot.clear" override def onClick(): Unit = clear() } } diff --git a/src/main/scala/ocelot/desktop/windows/OpenFMRadioWindow.scala b/src/main/scala/ocelot/desktop/windows/OpenFMRadioWindow.scala index 0567389..b24e561 100644 --- a/src/main/scala/ocelot/desktop/windows/OpenFMRadioWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/OpenFMRadioWindow.scala @@ -8,7 +8,8 @@ import ocelot.desktop.node.nodes.OpenFMRadioNode import ocelot.desktop.ui.layout.{AlignItems, Layout, LinearLayout} import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.window.BasicWindow -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} class OpenFMRadioWindow(radioNode: OpenFMRadioNode) extends BasicWindow { private val scale = 2f @@ -49,7 +50,7 @@ class OpenFMRadioWindow(radioNode: OpenFMRadioNode) extends BasicWindow { children :+= new PaddingBox( new Label { override def maximumSize: Size2D = Size2D(20, 20) - override def text: String = (radio.volume * 10).floor.toInt.toString + override def text: Message = (radio.volume * 10).floor.toInt.toString override def color: Color = Color.White }, Padding2D(2, 0, 0, 20), @@ -121,20 +122,20 @@ class OpenFMRadioWindow(radioNode: OpenFMRadioNode) extends BasicWindow { // Color title children :+= new Label { - override def text: String = "Screen color" + override def text: Message = m"label.window.radio.screenColor" override def color: Color = Color.White } // Text title children :+= new Label { - override def text: String = "Screen text" + override def text: Message = m"label.window.radio.screenText" override def color: Color = Color.White } }, Padding2D(10, 0, 0, 30), ) - // Color / text text inputs + // Color / text inputs children :+= new PaddingBox( new Widget { override protected val layout: Layout = new LinearLayout(this, @@ -177,7 +178,7 @@ class OpenFMRadioWindow(radioNode: OpenFMRadioNode) extends BasicWindow { // OpenFM logo children :+= new PaddingBox( new Label { - override def text: String = "OpenFM" + override def text: Message = "OpenFM" override def color: Color = Color.White }, Padding2D(22, 0, 0, 210), diff --git a/src/main/scala/ocelot/desktop/windows/RackWindow.scala b/src/main/scala/ocelot/desktop/windows/RackWindow.scala index 80f7caa..733de17 100644 --- a/src/main/scala/ocelot/desktop/windows/RackWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/RackWindow.scala @@ -12,7 +12,8 @@ import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.slot.RackMountableSlotWidget import ocelot.desktop.ui.widget.traits.HoverHighlight import ocelot.desktop.ui.widget.window.PanelWindow -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} import totoro.ocelot.brain.util.Direction import totoro.ocelot.brain.util.Direction.Direction @@ -46,7 +47,7 @@ class RackWindow(rackNode: RackNode) extends PanelWindow { ) } - override protected def title: String = "Rack" + override protected def title: Message = m"component.rack" private val mountableSlotWidgets: Array[RackMountableSlotWidget] = new Array[RackMountableSlotWidget](rackNode.rack.getSizeInventory) @@ -239,20 +240,20 @@ class RackWindow(rackNode: RackNode) extends PanelWindow { new Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) - def addLabel(data: String): Unit = { + def addLabel(data: Message): Unit = { children :+= new PaddingBox( new Label { - override def text: String = data + override def text: Message = data }, Padding2D(left = 20, bottom = 6), ) } - addLabel("Bottom") - addLabel("Top") - addLabel("Back") - addLabel("Right") - addLabel("Left") + addLabel(m"direction.bottom") + addLabel(m"direction.top") + addLabel(m"direction.back") + addLabel(m"direction.right") + addLabel(m"direction.left") // Relay enable button children :+= new PaddingBox( @@ -271,7 +272,7 @@ class RackWindow(rackNode: RackNode) extends PanelWindow { val x = position.x + 38 val y = position.y + size.height / 2 - 8 - val text = if (rackNode.rack.isRelayEnabled) "Enabled" else "Disabled" + val text = (if (rackNode.rack.isRelayEnabled) m"label.window.rack.enabled" else m"label.window.rack.disabled").toString g.background = Color.Transparent diff --git a/src/main/scala/ocelot/desktop/windows/RaidWindow.scala b/src/main/scala/ocelot/desktop/windows/RaidWindow.scala index 02544a5..ced12b8 100644 --- a/src/main/scala/ocelot/desktop/windows/RaidWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/RaidWindow.scala @@ -9,11 +9,12 @@ import ocelot.desktop.ui.layout.{AlignItems, Layout, LinearLayout} import ocelot.desktop.ui.widget._ import ocelot.desktop.ui.widget.slot.HddSlotWidget import ocelot.desktop.ui.widget.window.PanelWindow -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} import totoro.ocelot.brain.util.Tier class RaidWindow(raidNode: RaidNode) extends PanelWindow { - override protected def title: String = s"Raid ${raidNode.raid.filesystem.map(_.node.address).getOrElse("")}" + override protected def title: Message = s"${m"component.raid"} ${raidNode.raid.filesystem.map(_.node.address).getOrElse("")}" override def minimumSize: Size2D = Size2D(350, 147) override def maximumSize: Size2D = minimumSize @@ -80,15 +81,15 @@ class RaidWindow(raidNode: RaidNode) extends PanelWindow { gap = 2, ) - def addLabel(data: String): Unit = { + def addLabel(data: Message): Unit = { children :+= new Label { - override def text: String = data + override def text: Message = data override def color: Color = ColorScheme("LabelError") } } - addLabel("Adding a disk wipes it.") - addLabel("Removing a disk wipes the raid.") + addLabel(m"label.window.raid.warning.addingDisk") + addLabel(m"label.window.raid.warning.removingDisk") }, Padding2D(left = 5), ) diff --git a/src/main/scala/ocelot/desktop/windows/RelayWindow.scala b/src/main/scala/ocelot/desktop/windows/RelayWindow.scala index 53bf2fb..6e35d48 100644 --- a/src/main/scala/ocelot/desktop/windows/RelayWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/RelayWindow.scala @@ -10,7 +10,8 @@ import ocelot.desktop.ui.layout.{AlignItems, JustifyContent, Layout, LinearLayou import ocelot.desktop.ui.widget.slot.{CardSlotWidget, CpuSlotWidget, HddSlotWidget, MemorySlotWidget} import ocelot.desktop.ui.widget.window.PanelWindow import ocelot.desktop.ui.widget.{Label, Widget} -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} import ocelot.desktop.windows.RelayWindow.{LabelWidth, TransferRateFormat} import totoro.ocelot.brain.util.Tier import totoro.ocelot.brain.util.Tier.Tier @@ -18,10 +19,10 @@ import totoro.ocelot.brain.util.Tier.Tier import java.text.DecimalFormat class RelayWindow(val node: RelayNode) extends PanelWindow { - override protected def title: String = "Relay" + override protected def title: Message = m"component.relay" - private def makeLabel(_text: => String): Label = new Label { - override def text: String = _text + private def makeLabel(_text: => Message): Label = new Label { + override def text: Message = _text override def minimumSize: Size2D = super.minimumSize.copy(width = LabelWidth) @@ -40,10 +41,10 @@ class RelayWindow(val node: RelayNode) extends PanelWindow { gap = 8f, ) - children :+= makeLabel("Cycle rate") + children :+= makeLabel(m"label.window.relay.cycleRate") children :+= new Label { - override def text: String = s"${TransferRateFormat.format(20f / node.relay.relayDelay)}" + override def text: Message = s"${TransferRateFormat.format(20f / node.relay.relayDelay)}" } children :+= new CpuSlotWidget(node.Slot(node.relay.cpuSlot.index), Tier.Three) { @@ -60,10 +61,10 @@ class RelayWindow(val node: RelayNode) extends PanelWindow { gap = 8f, ) - children :+= makeLabel("Packets / cycle") + children :+= makeLabel(m"label.window.relay.packetsPerCycle") children :+= new Label { - override def text: String = s"${node.relay.packetsPerCycleAvg()} / ${node.relay.relayAmount}" + override def text: Message = s"${node.relay.packetsPerCycleAvg()} / ${node.relay.relayAmount}" override def color: Color = thresholdBasedColor( node.relay.packetsPerCycleAvg(), @@ -86,10 +87,10 @@ class RelayWindow(val node: RelayNode) extends PanelWindow { gap = 8f, ) - children :+= makeLabel("Queue size") + children :+= makeLabel(m"label.window.relay.queueSize") children :+= new Label { - override def text: String = s"${node.relay.queue.size} / ${node.relay.maxQueueSize}" + override def text: Message = s"${node.relay.queue.size} / ${node.relay.maxQueueSize}" override def color: Color = thresholdBasedColor( node.relay.queue.size, diff --git a/src/main/scala/ocelot/desktop/windows/ScreenWindow.scala b/src/main/scala/ocelot/desktop/windows/ScreenWindow.scala index d61a25c..df49eb8 100644 --- a/src/main/scala/ocelot/desktop/windows/ScreenWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/ScreenWindow.scala @@ -11,6 +11,7 @@ import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.event.sources.{KeyEvents, MouseEvents} import ocelot.desktop.ui.event.{DragEvent, KeyEvent, MouseEvent, ScrollEvent} import ocelot.desktop.ui.widget.window.BasicWindow +import ocelot.desktop.util.Message.LocalizedMessage import ocelot.desktop.util.{DrawUtils, Logging} import ocelot.desktop.windows.ScreenWindow._ import ocelot.desktop.{ColorScheme, OcelotDesktop, Settings} @@ -228,8 +229,8 @@ class ScreenWindow(screenNode: ScreenNode) extends BasicWindow with Logging { super.update() if (scaleDragPoint.isDefined || scaleDragRegion.contains(UiHandler.mousePosition)) { - root.get.statusBar.addMouseEntry("icons/DragLMB", "Scale screen") - root.get.statusBar.addKeyMouseEntry("icons/DragLMB", "SHIFT", "Scale screen (magnify)") + root.get.statusBar.addMouseEntry("icons/DragLMB", m"label.bar.scaleScreen") + root.get.statusBar.addKeyMouseEntry("icons/DragLMB", "SHIFT", m"label.bar.scaleScreenMagnify") } val currentMousePos = convertMousePos(UiHandler.mousePosition) diff --git a/src/main/scala/ocelot/desktop/windows/TapeDriveWindow.scala b/src/main/scala/ocelot/desktop/windows/TapeDriveWindow.scala index 9be7ff1..7a76863 100644 --- a/src/main/scala/ocelot/desktop/windows/TapeDriveWindow.scala +++ b/src/main/scala/ocelot/desktop/windows/TapeDriveWindow.scala @@ -12,14 +12,15 @@ import ocelot.desktop.ui.widget.slot.SlotWidget import ocelot.desktop.ui.widget.traits.HoverHighlight import ocelot.desktop.ui.widget.window.PanelWindow import ocelot.desktop.ui.widget.{IconButton, PaddingBox, Widget} -import ocelot.desktop.util.Orientation +import ocelot.desktop.util.Message.LocalizedMessage +import ocelot.desktop.util.{Message, Orientation} import ocelot.desktop.windows.TapeDriveWindow.{TapeButtonSound, TapeEject, TapeInsert} import totoro.ocelot.brain.entity.TapeDriveState class TapeDriveWindow(val tapeDriveNode: TapeDriveNode) extends PanelWindow { private val playbackOverlayColor: RGBAColorNorm = RGBAColorNorm(1, 1, 1, 0.01f) - override protected def title: String = s"Tape Drive ${tapeDriveNode.tapeDrive.node.address}" + override protected def title: Message = s"${m"component.tapeDrive"} ${tapeDriveNode.tapeDrive.node.address}" override def titleMaxLength: Int = 35 override def minimumSize: Size2D = Size2D(352, 182) @@ -68,10 +69,10 @@ class TapeDriveWindow(val tapeDriveNode: TapeDriveNode) extends PanelWindow { text = tapeDriveNode.tapeDrive.storageName if (text.isEmpty) - text = "Unnamed tape" + text = m"label.window.tapeDrive.unnamedTape".toString } else { g.foreground = Color.Red - text = "No tape" + text = m"label.window.tapeDrive.noTape".toString } // Clipping text if it's too long