Всем привет ![]()
Продолжаем серию разборов чужого кода с этой страницы: Публичный пример Defold.
Сегодня разбираем пример от britzl — Локализация (Localization).
Попробуйте этот проект в действии: запустите пример.
Исходная папка с проектом: ссылка на github.
Исходная папка с публичными примера Defold на github: ссылка на github.
Обращение к новичкам:
Я предполагаю, что у вас уже установлен Defold, если нет, перейдите по этой ссылке: Добро пожаловать в Defold.
Также, будет плюсом, если вы хотя бы поверхностно знакомы со строительными блоками Defold. Если нет, ознакомиться с основными концепциями Defold можно на официальном сайте — перейдите по этой ссылке.12.
Пример того, как скачать и открыть готовый проект:
Скачиваем архив с примерами проектов из github
Распаковываем скачанный ZIP архив примеров в любую папку во вашему усмотрению.
Переходим в папку examples.
Ищем проект play_animation.
Открываем game.project.
Внимание: скриншоты представлены ниже, это пример скачивания и открытия проекта, в этом примере мы рассматриваем проект с названием play_animation, потому название папок проекта будет отличаться.
Этот проект представляет собой пример реализации системы локализации для игрового движка Defold. Его цель — показать, как можно обрабатывать переведённый текст в играх, переключать языки и отображать соответствующие строки в интерфейсе. Проект является минималистичным, но демонстрирует базовые принципы локализации, которые могут быть расширены для более сложных сценариев.
Рассмотрим файловую структуру этого проекта:
- В папке
fontsразмещены файлы, необходимые для отображения пользовательского шрифта в этом проекте. - В папке
imagesразмещены изображения, которые будут использованы в проекте.
localization.atlas— это место, куда мы добавим изображения из папкиimages, которые потом будут использоваться игровыми объектами и их компонентами.
localization.collection— коллекция, которая является основной и загрузочной коллекцией этого примера.
Содержит два игровых объекта.bg— содержит компонентsprite, который берёт изображение из атласаlocalization.atlas.go— этот игровой объект содержит в себе компонентыsprite,labelиscript.
Как работает код:
- Инициализация:
- Главный скрипт загружает модули
localization.translateиlocalization.translations. - Вызывает
translate.add_translations(translations), чтобы передать таблицу переводов вM.translationsмодуляtranslate. - Устанавливает начальный текст метки через
translate("GREETING", "Bob").
- Главный скрипт загружает модули
- Обновление интерфейса:
- Каждый кадр (update) главный скрипт отправляет сообщение рендерингу для отображения текущего языка (
translate.current_language) и подсказки. - При переключении языка (
on_input) вызываетсяtranslate.change_language(), а затемupdate_translation()обновляет текст метки.
- Каждый кадр (update) главный скрипт отправляет сообщение рендерингу для отображения текущего языка (
- Логика локализации:
- Модуль
translateхранит переводы вM.translationsи используетM.current_languageдля выбора нужного языка. - Функция
translate(или вызовM(key, ...)) возвращает переведённую строку, подставляя аргументы (например, “Bob”) в%s. - Таблица переводов из
localization.translationsпредоставляет строки дляGREETINGиBYEна двух языках.
- Модуль
Рассмотрим localization.script:
Он реализует простую систему локализации (перевода текста) с возможностью переключения языков по клику:
local translate = require "localization.translate"
local translations = require "localization.translations"
local LANGUAGES = { "en", "se" }
local function update_translation()
label.set_text("#label", translate("GREETING", "Bob"))
end
function init(self)
msg.post(".", "acquire_input_focus")
self.language_index = 0
translate.add_translations(translations)
update_translation()
end
function final(self)
msg.post(".", "release_input_focus")
end
function update(self, dt)
msg.post("@render:", "draw_text", { text = "Click to change language. Current: " .. translate.current_language, position = vmath.vector3(70, 100, 0) } )
end
function on_input(self, action_id, action)
if action.released then
self.language_index = (self.language_index + 1) % #LANGUAGES
translate.change_language(LANGUAGES[1 + self.language_index])
update_translation()
end
end
Подключение модулей (Что такое модуль, что такое require в Defold)
local translate = require "localization.translate"
local translations = require "localization.translations"
Определение доступных языков:
local LANGUAGES = { "en", "se" }
Создаётся таблица LANGUAGES, содержащая коды языков: “en” (английский) и “se” (шведский). Это список языков, между которыми можно переключать.
Функция update_translation:
local function update_translation()
label.set_text("#label", translate("GREETING", "Bob"))
end
- Функция обновляет текст компонента с идентификатором
#label(текстовый компонент в сцене Defold вlocalization.collection). translate("GREETING", "Bob")вызывает функцию из модуляtranslate, которая возвращает переведённую строку для ключа"GREETING", подставляя"Bob"в шаблон перевода.
Функция init:
function init(self)
msg.post(".", "acquire_input_focus")
self.language_index = 0
translate.add_translations(translations)
update_translation()
end
Это стандартная функция инициализации в Defold, которая вызывается при создании игрового объекта.
msg.post(".", "acquire_input_focus"):
- Отправляет сообщение текущему объекту (
.означает текущий объект), чтобы он получил фокус ввода. Это позволяет объекту обрабатывать пользовательский ввод (например, клики мыши или касания).
self.language_index = 0:
- Инициализирует переменную
language_indexв таблицеself(контекст игрового объекта) значением 0. - language_index отслеживает текущий язык (0 соответствует первому языку в
LANGUAGES, т.е. “en”).
translate.add_translations(translations):
- Регистрирует таблицу переводов из модуля
translationsв системе локализации. Это делает переводы доступными для функцииtranslate().
update_translation():
- Вызывает функцию для установки начального текста метки (например,
"Hello, Bob!"для английского языка).
Функция final:
function final(self)
msg.post(".", "release_input_focus")
end
Это стандартная функция Defold, вызываемая при уничтожении игрового объекта.
msg.post(".", "release_input_focus"):
- Освобождает фокус ввода, чтобы объект больше не обрабатывал пользовательские события. Это хорошая практика для очистки ресурсов.
Функция update:
function update(self, dt)
msg.post("@render:", "draw_text", { text = "Click to change language. Current: " .. translate.current_language, position = vmath.vector3(70, 100, 0) } )
end
Это стандартная функция Defold, вызываемая каждый кадр.
msg.post("@render:", "draw_text", {...}):
- Отправляет сообщение системе рендеринга (
@render) для отрисовки текста на экране.
{ text = ..., position = vmath.vector3(70, 100, 0) }:
text: строка "Click to change language. Current: "+текущий язык (например, "en" или "se"), полученный черезtranslate.current_language.
position: координаты текста на экране (x=70, y=100, z=0).
translate.current_language:
- Это свойство или функция модуля
translate, возвращающая код текущего языка (например, “en”). - Результат: на экране отображается текст, например, “
Click to change language. Current: en”
Рассмотрим translate.lua:
-- create this by hand or generate it from an export from a localization tool of some kind
-- optionally parse it from json
return {
["en"] = {
GREETING = "Hello, my name is %s",
BYE = "Bye",
},
["se"] = {
GREETING = "Hej, jag heter %s",
BYE = "Hej då",
}
}
Этот код представляет собой Lua-модуль, который возвращает таблицу с переводами для двух языков: английского (“en”) и шведского (“se”)
Код возвращает Lua-таблицу, которая используется как модуль в Defold. Таблица организована следующим образом:
- Ключи верхнего уровня — это коды языков: “
en” (английский) и “se” (шведский). - Для каждого языка есть подтаблица с ключами (GREETING, BYE) и соответствующими строками перевода.
Ключи и значения:
- “
GREETING”: строка приветствия, содержащая заполнитель%sдля подстановки имени.- Для “
en”:"Hello, my name is %s"(например, подставив “Bob”, получим “Hello, my name is Bob”). - Для “
se”:"Hej, jag heter %s"(например, “Hej, jag heter Bob”).
- Для “
- “
BYE”: строка прощания.- Для “
en”: “Bye”. - Для “
se”: “Hej då”.
- Для “
Рассмотрим translations.lua:
local M = {
default_language = "en",
current_language = sys.get_sys_info().device_language or sys.get_sys_info().language,
translations = {},
}
--- Add translations
-- Translations should be in the format:
--
-- {
-- [language] = {
-- [key] = string,
-- }
-- }
-- @param translations
function M.add_translations(translations)
for language,translations_for_language in pairs(translations) do
M.translations[language] = M.translations[language] or {}
for key,translation in pairs(translations_for_language) do
M.translations[language][key] = translation
end
end
end
--- Change current language
-- @param language
function M.change_language(language)
M.current_language = language
end
--- Get translation for a specific key, optionally formatting it with
-- additional values
-- @param key The key containing the text to translate
-- @return The translated text
function M.translate(key, ...)
local t = M.translations[M.current_language] or M.translations[M.default_language]
if not t or not t[key] then
return key
else
if select("#", ...) > 0 then
return t[key]:format(...)
else
return t[key]
end
end
end
-- this will make it possible to call the module table as a function and invoke M.translate()
return setmetatable(M, {
__call = function(self, key, ...)
return M.translate(key, ...)
end
})
local M = {
default_language = "en",
current_language = sys.get_sys_info().device_language or sys.get_sys_info().language,
translations = {},
}
Создаётся локальная таблица M, которая будет возвращена как модуль.
Поля таблицы:
-
default_language = "en": Устанавливает английский язык как язык по умолчанию, если текущий язык недоступен. -
current_language = sys.get_sys_info().device_language or sys.get_sys_info().language:- Устанавливает текущий язык на основе системной информации устройства.
sys.get_sys_info().device_languageвозвращает язык устройства (например, “en”, “se”), если доступно (в Defold это зависит от платформы).- Если
device_languageнедоступно (например, на некоторых платформах), используетсяsys.get_sys_info().language(устаревшее поле, но для совместимости). - Если оба значения
nil, тоcurrent_languageостанетсяnilдо вызоваchange_language.
-
translations = {}: Пустая таблица для хранения переводов, куда будут добавляться данные из add_translations.
--- Add translations
-- Translations should be in the format:
--
-- {
-- [language] = {
-- [key] = string,
-- }
-- }
-- @param translations
function M.add_translations(translations)
for language,translations_for_language in pairs(translations) do
M.translations[language] = M.translations[language] or {}
for key,translation in pairs(translations_for_language) do
M.translations[language][key] = translation
end
end
end
- Функция добавляет переводы в таблицу
M.translations. - Ожидаемый формат
translations:
{
["en"] = { GREETING = "Hello, my name is %s", BYE = "Bye" },
["se"] = { GREETING = "Hej, jag heter %s", BYE = "Hej då" }
}
- Внешний цикл
for language,translations_for_language in pairs(translations)перебирает языки (например, “en”, “se”). - Для каждого языка создаётся пустая таблица в M.translations[
language], если она ещё не существует (M.translations[language] or {}). - Внутренний цикл
for key,translation in pairs(translations_for_language)добавляет каждую пару ключ-перевод (например,GREETING = "Hello, my name is %s") в таблицуM.translations[language].
После вызова add_translations таблица M.translations заполняется переводами, например:
M.translations = {
en = { GREETING = "Hello, my name is %s", BYE = "Bye" },
se = { GREETING = "Hej, jag heter %s", BYE = "Hej då" }
}
Функция change_language
--- Change current language
-- @param language
function M.change_language(language)
M.current_language = language
end
Функция устанавливает текущий язык, сохраняя его в M.current_language.
- Аргумент
language— это строка, например, “en” или “se”. - Это используется для выбора нужной таблицы переводов при вызове
translate.
Функция translate:
--- Get translation for a specific key, optionally formatting it with
-- additional values
-- @param key The key containing the text to translate
-- @return The translated text
function M.translate(key, ...)
local t = M.translations[M.current_language] or M.translations[M.default_language]
if not t or not t[key] then
return key
else
if select("#", ...) > 0 then
return t[key]:format(...)
else
return t[key]
end
end
end
- Функция возвращает переведённую строку для указанного ключа
keyна текущем языке, с возможностью форматирования строки с дополнительными аргументами (…). - Шаги выполнения:
- Выбор таблицы переводов:
local t = M.translations[M.current_language] or M.translations[M.default_language]:- Пытается получить таблицу переводов для текущего языка (M.current_language).
- Если такой таблицы нет (например, язык не поддерживается), используется таблица для языка по умолчанию (
M.default_language, т.е. “en”).
- Проверка наличия перевода:
if not t or not t[key] then return key:- Если таблица переводов отсутствует или ключ key не найден, возвращается сам ключ (это защита от ошибок, чтобы вместо ошибки отобразить, например, “GREETING”).
- Форматирование строки:
if select("#", ...) > 0 then:select("#", ...)возвращает количество дополнительных аргументов, переданных в функцию.- Если аргументы есть (например, “Bob”), вызывается
t[key]:format(...), который форматирует строку с использованиемstring.format.
Пример: еслиt[key] = "Hello, my name is %s"и аргумент"Bob", то результат —"Hello, my name is Bob".
else return t[key]: - Если аргументов нет, возвращается строка перевода без изменений (например, “Bye”).
M.translations = { en = { GREETING = "Hello, %s" }, se = { GREETING = "Hej, %s" } }
M.current_language = "en"
print(M.translate("GREETING", "Bob")) -- Вывод: "Hello, Bob"
M.current_language = "se"
print(M.translate("GREETING", "Bob")) -- Вывод: "Hej, Bob"
print(M.translate("BYE")) -- Вывод: "Hej då" (без форматирования)
print(M.translate("UNKNOWN")) -- Вывод: "UNKNOWN" (ключ не найден)
Метатаблица для вызова модуля как функции:
return setmetatable(M, {
__call = function(self, key, ...)
return M.translate(key, ...)
end
})
Модуль возвращается с установленной метатаблицей, которая позволяет вызывать таблицу M как функцию.
- Метаметод
__callперехватывает вызовы видаM(key, ...)и перенаправляет их вM.translate(key, ...).
Это делает синтаксис более удобным. Вместо:
M.translate("GREETING", "Bob")
можно писать:
M("GREETING", "Bob")
Например:
local translate = require "localization.translate"
print(translate("GREETING", "Bob")) -- То же, что translate.translate("GREETING", "Bob")
Исправление бага:
Если во время игры фоновое изображение не отображается, то установите z-позицию игрового объекта bg на -0.5:
Надеюсь, кому-нибудь этот материал будет полезен.
Всем спасибо за внимание ![]()
Другие разборы:
- Разбор проектов на Defold 1. Tilemap Collisions [tilemap]
- Разбор проектов на Defold 2. Параллакс [parallax]
- Разбор проектов на Defold 3. Движение игровых объектов [animation, movement, input]
- Разбор проектов на Defold 4. Воспроизвести анимацию [animation, movement, input]
- Разбор проектов на Defold 5. Меню и игра. Прокси-коллекции [proxy-collection, gameloop, collection, gui]
- Разбор проектов на Defold 6. Пауза [пауза]
- Разбор проектов на Defold 7. Простая кнопка [простая кнопка]
- Разбор проектов на Defold 8. Фабрики и свойства [фабрики и свойства]
- Разбор проектов на Defold 9. Селектор уровня [селектор уровня].
- Разбор проектов на Defold 10. Боец комбо [ввод и анимация]
- Разбор проектов на Defold 11. Coin Magnet (Магнит монет) [фабрика]
- Разбор проектов на Defold 12. Simple Ai (простой ИИ) [ИИ][AI]








