mirror of
https://github.com/minetest/minetest.git
synced 2024-11-26 17:43:45 +01:00
Add support for translating content titles and descriptions (#12208)
This commit is contained in:
parent
57de599a29
commit
b4be483d3e
@ -150,6 +150,8 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
|
||||
toadd.virtual_path = mod_virtual_path
|
||||
toadd.type = "mod"
|
||||
|
||||
pkgmgr.update_translations({ toadd })
|
||||
|
||||
-- Check modpack.txt
|
||||
-- Note: modpack.conf is already checked above
|
||||
local modpackfile = io.open(mod_path .. DIR_DELIM .. "modpack.txt")
|
||||
@ -189,6 +191,8 @@ function pkgmgr.get_texture_packs()
|
||||
load_texture_packs(txtpath_system, retval)
|
||||
end
|
||||
|
||||
pkgmgr.update_translations(retval)
|
||||
|
||||
table.sort(retval, function(a, b)
|
||||
return a.title:lower() < b.title:lower()
|
||||
end)
|
||||
@ -775,6 +779,29 @@ function pkgmgr.update_gamelist()
|
||||
table.sort(pkgmgr.games, function(a, b)
|
||||
return a.title:lower() < b.title:lower()
|
||||
end)
|
||||
pkgmgr.update_translations(pkgmgr.games)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function pkgmgr.update_translations(list)
|
||||
for _, item in ipairs(list) do
|
||||
local info = core.get_content_info(item.path)
|
||||
assert(info.path)
|
||||
assert(info.textdomain)
|
||||
|
||||
assert(not item.is_translated)
|
||||
item.is_translated = true
|
||||
|
||||
if info.title and info.title ~= "" then
|
||||
item.title = core.get_content_translation(info.path, info.textdomain,
|
||||
core.translate(info.textdomain, info.title))
|
||||
end
|
||||
|
||||
if info.description and info.description ~= "" then
|
||||
item.description = core.get_content_translation(info.path, info.textdomain,
|
||||
core.translate(info.textdomain, info.description))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -114,12 +114,13 @@ local function get_formspec(tabview, name, tabdata)
|
||||
modscreenshot = defaulttexturedir .. "no_screenshot.png"
|
||||
end
|
||||
|
||||
local info = core.get_content_info(selected_pkg.path)
|
||||
local desc = fgettext("No package description available")
|
||||
if info.description and info.description:trim() ~= "" then
|
||||
desc = core.formspec_escape(info.description)
|
||||
if selected_pkg.description and selected_pkg.description:trim() ~= "" then
|
||||
desc = core.formspec_escape(selected_pkg.description)
|
||||
end
|
||||
|
||||
local info = core.get_content_info(selected_pkg.path)
|
||||
|
||||
local title_and_name
|
||||
if selected_pkg.type == "game" then
|
||||
title_and_name = selected_pkg.name
|
||||
|
@ -61,7 +61,8 @@ The game directory can contain the following files:
|
||||
* `game.conf`, with the following keys:
|
||||
* `title`: Required, a human-readable title to address the game, e.g. `title = Minetest Game`.
|
||||
* `name`: (Deprecated) same as title.
|
||||
* `description`: Short description to be shown in the content tab
|
||||
* `description`: Short description to be shown in the content tab.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
* `allowed_mapgens = <comma-separated mapgens>`
|
||||
e.g. `allowed_mapgens = v5,v6,flat`
|
||||
Mapgens not in this list are removed from the list of mapgens for the
|
||||
@ -87,10 +88,11 @@ The game directory can contain the following files:
|
||||
`enable_damage`, `creative_mode`, `enable_server`.
|
||||
* `map_persistent`: Specifies whether newly created worlds should use
|
||||
a persistent map backend. Defaults to `true` (= "sqlite3")
|
||||
* `author`: The author of the game. It only appears when downloaded from
|
||||
ContentDB.
|
||||
* `author`: The author's ContentDB username.
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is
|
||||
an internal ID used to track versions.
|
||||
* `textdomain`: Textdomain used to translate description. Defaults to game id.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
* `minetest.conf`:
|
||||
Used to set default settings when running this game.
|
||||
* `settingtypes.txt`:
|
||||
@ -156,13 +158,14 @@ The file is a key-value store of modpack details.
|
||||
|
||||
* `name`: The modpack name. Allows Minetest to determine the modpack name even
|
||||
if the folder is wrongly named.
|
||||
* `title`: A human-readable title to address the modpack. See [Translating content meta](#translating-content-meta).
|
||||
* `description`: Description of mod to be shown in the Mods tab of the main
|
||||
menu.
|
||||
* `author`: The author of the modpack. It only appears when downloaded from
|
||||
ContentDB.
|
||||
menu. See [Translating content meta](#translating-content-meta).
|
||||
* `author`: The author's ContentDB username.
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is an
|
||||
internal ID used to track versions.
|
||||
* `title`: A human-readable title to address the modpack.
|
||||
* `textdomain`: Textdomain used to translate title and description. Defaults to modpack name.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
|
||||
Note: to support 0.4.x, please also create an empty modpack.txt file.
|
||||
|
||||
@ -201,17 +204,18 @@ A `Settings` file that provides meta information about the mod.
|
||||
|
||||
* `name`: The mod name. Allows Minetest to determine the mod name even if the
|
||||
folder is wrongly named.
|
||||
* `title`: A human-readable title to address the mod. See [Translating content meta](#translating-content-meta).
|
||||
* `description`: Description of mod to be shown in the Mods tab of the main
|
||||
menu.
|
||||
menu. See [Translating content meta](#translating-content-meta).
|
||||
* `depends`: A comma separated list of dependencies. These are mods that must be
|
||||
loaded before this mod.
|
||||
* `optional_depends`: A comma separated list of optional dependencies.
|
||||
Like a dependency, but no error if the mod doesn't exist.
|
||||
* `author`: The author of the mod. It only appears when downloaded from
|
||||
ContentDB.
|
||||
* `author`: The author's ContentDB username.
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is an
|
||||
internal ID used to track versions.
|
||||
* `title`: A human-readable title to address the mod.
|
||||
* `textdomain`: Textdomain used to translate title and description. Defaults to modname.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
|
||||
### `screenshot.png`
|
||||
|
||||
@ -4135,6 +4139,46 @@ the table returned by `minetest.get_player_information(name)`.
|
||||
IMPORTANT: This functionality should only be used for sorting, filtering or similar purposes.
|
||||
You do not need to use this to get translated strings to show up on the client.
|
||||
|
||||
Translating content meta
|
||||
------------------------
|
||||
|
||||
You can translate content meta, such as `title` and `description`, by placing
|
||||
translations in a `locale/DOMAIN.LANG.tr` file. The textdomain defaults to the
|
||||
content name, but can be customised using `textdomain` in the content's .conf.
|
||||
|
||||
### Mods and Texture Packs
|
||||
|
||||
Say you have a mod called `mymod` with a short description in mod.conf:
|
||||
|
||||
```
|
||||
description = This is the short description
|
||||
```
|
||||
|
||||
Minetest will look for translations in the `mymod` textdomain as there's no
|
||||
textdomain specified in mod.conf. For example, `mymod/locale/mymod.fr.tr`:
|
||||
|
||||
```
|
||||
# textdomain:mymod
|
||||
This is the short description=Voici la description succincte
|
||||
```
|
||||
|
||||
### Games and Modpacks
|
||||
|
||||
For games and modpacks, Minetest will look for the textdomain in all mods.
|
||||
|
||||
Say you have a game called `mygame` with the following game.conf:
|
||||
|
||||
```
|
||||
description = This is the game's short description
|
||||
textdomain = mygame
|
||||
```
|
||||
|
||||
Minetest will then look for the textdomain `mygame` in all mods, for example,
|
||||
`mygame/mods/anymod/locale/mygame.fr.tr`. Note that it is still recommended that your
|
||||
textdomain match the mod name, but this isn't required.
|
||||
|
||||
|
||||
|
||||
Perlin noise
|
||||
============
|
||||
|
||||
|
@ -323,6 +323,7 @@ Package - content which is downloadable from the content db, may or may not be i
|
||||
description = "description",
|
||||
author = "author",
|
||||
path = "path/to/content",
|
||||
textdomain = "textdomain", -- textdomain to translate title / description with
|
||||
depends = {"mod", "names"}, -- mods only
|
||||
optional_depends = {"mod", "names"}, -- mods only
|
||||
}
|
||||
@ -340,6 +341,13 @@ Package - content which is downloadable from the content db, may or may not be i
|
||||
error_message = "", -- message or nil
|
||||
}
|
||||
```
|
||||
* `core.get_content_translation(path, domain, string)`
|
||||
* Translates `string` using `domain` in content directory at `path`.
|
||||
* Textdomains will be found by looking through all locale folders.
|
||||
* String should contain translation markup from `core.translate(textdomain, ...)`.
|
||||
* Ex: `core.get_content_translation("mods/mymod", "mymod", core.translate("mymod", "Hello World"))`
|
||||
will translate "Hello World" into the current user's language
|
||||
using `mods/mymod/locale/mymod.fr.tr`.
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
@ -25,8 +25,14 @@ texture pack. The name must not be “base”.
|
||||
### `texture_pack.conf`
|
||||
A key-value config file with the following keys:
|
||||
|
||||
* `title` - human readable title
|
||||
* `name`: The texture pack name. Allows Minetest to determine the texture pack name even if
|
||||
the folder is wrongly named.
|
||||
* `title` - human-readable title
|
||||
* `description` - short description, shown in the content tab
|
||||
* `author`: The author's ContentDB username.
|
||||
* `textdomain`: Textdomain used to translate title and description.
|
||||
Defaults to the texture pack name.
|
||||
See [Translating content meta](lua_api.md#translating-content-meta).
|
||||
|
||||
### `description.txt`
|
||||
**Deprecated**, you should use texture_pack.conf instead.
|
||||
@ -205,7 +211,8 @@ Here are targets you can choose from:
|
||||
Nodes support all targets, but other items only support 'inventory'
|
||||
and 'wield'.
|
||||
|
||||
¹ : `N` is an integer [0,255]. Sets align_style = "world" and scale = N on the tile, refer to lua_api.md for details.
|
||||
¹ : `N` is an integer [0,255]. Sets align_style = "world" and scale = N on the tile,
|
||||
refer to lua_api.md for details.
|
||||
|
||||
### Using the special targets
|
||||
|
||||
|
@ -24,68 +24,59 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "filesys.h"
|
||||
#include "settings.h"
|
||||
|
||||
enum ContentType
|
||||
ContentType getContentType(const std::string &path)
|
||||
{
|
||||
ECT_UNKNOWN,
|
||||
ECT_MOD,
|
||||
ECT_MODPACK,
|
||||
ECT_GAME,
|
||||
ECT_TXP
|
||||
};
|
||||
|
||||
ContentType getContentType(const ContentSpec &spec)
|
||||
{
|
||||
std::ifstream modpack_is((spec.path + DIR_DELIM + "modpack.txt").c_str());
|
||||
std::ifstream modpack_is((path + DIR_DELIM + "modpack.txt").c_str());
|
||||
if (modpack_is.good()) {
|
||||
modpack_is.close();
|
||||
return ECT_MODPACK;
|
||||
return ContentType::MODPACK;
|
||||
}
|
||||
|
||||
std::ifstream modpack2_is((spec.path + DIR_DELIM + "modpack.conf").c_str());
|
||||
std::ifstream modpack2_is((path + DIR_DELIM + "modpack.conf").c_str());
|
||||
if (modpack2_is.good()) {
|
||||
modpack2_is.close();
|
||||
return ECT_MODPACK;
|
||||
return ContentType::MODPACK;
|
||||
}
|
||||
|
||||
std::ifstream init_is((spec.path + DIR_DELIM + "init.lua").c_str());
|
||||
std::ifstream init_is((path + DIR_DELIM + "init.lua").c_str());
|
||||
if (init_is.good()) {
|
||||
init_is.close();
|
||||
return ECT_MOD;
|
||||
return ContentType::MOD;
|
||||
}
|
||||
|
||||
std::ifstream game_is((spec.path + DIR_DELIM + "game.conf").c_str());
|
||||
std::ifstream game_is((path + DIR_DELIM + "game.conf").c_str());
|
||||
if (game_is.good()) {
|
||||
game_is.close();
|
||||
return ECT_GAME;
|
||||
return ContentType::GAME;
|
||||
}
|
||||
|
||||
std::ifstream txp_is((spec.path + DIR_DELIM + "texture_pack.conf").c_str());
|
||||
std::ifstream txp_is((path + DIR_DELIM + "texture_pack.conf").c_str());
|
||||
if (txp_is.good()) {
|
||||
txp_is.close();
|
||||
return ECT_TXP;
|
||||
return ContentType::TXP;
|
||||
}
|
||||
|
||||
return ECT_UNKNOWN;
|
||||
return ContentType::UNKNOWN;
|
||||
}
|
||||
|
||||
void parseContentInfo(ContentSpec &spec)
|
||||
{
|
||||
std::string conf_path;
|
||||
|
||||
switch (getContentType(spec)) {
|
||||
case ECT_MOD:
|
||||
switch (getContentType(spec.path)) {
|
||||
case ContentType::MOD:
|
||||
spec.type = "mod";
|
||||
conf_path = spec.path + DIR_DELIM + "mod.conf";
|
||||
break;
|
||||
case ECT_MODPACK:
|
||||
case ContentType::MODPACK:
|
||||
spec.type = "modpack";
|
||||
conf_path = spec.path + DIR_DELIM + "modpack.conf";
|
||||
break;
|
||||
case ECT_GAME:
|
||||
case ContentType::GAME:
|
||||
spec.type = "game";
|
||||
conf_path = spec.path + DIR_DELIM + "game.conf";
|
||||
break;
|
||||
case ECT_TXP:
|
||||
case ContentType::TXP:
|
||||
spec.type = "txp";
|
||||
conf_path = spec.path + DIR_DELIM + "texture_pack.conf";
|
||||
break;
|
||||
@ -104,6 +95,15 @@ void parseContentInfo(ContentSpec &spec)
|
||||
if (spec.type != "game" && conf.exists("name"))
|
||||
spec.name = conf.get("name");
|
||||
|
||||
if (conf.exists("title"))
|
||||
spec.title = conf.get("title");
|
||||
|
||||
if (spec.type == "game") {
|
||||
if (spec.title.empty())
|
||||
spec.title = spec.name;
|
||||
spec.name = "";
|
||||
}
|
||||
|
||||
if (conf.exists("description"))
|
||||
spec.desc = conf.get("description");
|
||||
|
||||
@ -112,8 +112,17 @@ void parseContentInfo(ContentSpec &spec)
|
||||
|
||||
if (conf.exists("release"))
|
||||
spec.release = conf.getS32("release");
|
||||
|
||||
if (conf.exists("textdomain"))
|
||||
spec.textdomain = conf.get("textdomain");
|
||||
}
|
||||
|
||||
if (spec.name.empty())
|
||||
spec.name = fs::GetFilenameFromPath(spec.path.c_str());
|
||||
|
||||
if (spec.textdomain.empty())
|
||||
spec.textdomain = spec.name;
|
||||
|
||||
if (spec.desc.empty()) {
|
||||
std::ifstream is((spec.path + DIR_DELIM + "description.txt").c_str());
|
||||
spec.desc = std::string((std::istreambuf_iterator<char>(is)),
|
||||
|
@ -22,6 +22,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "convert_json.h"
|
||||
#include "irrlichttypes.h"
|
||||
|
||||
enum class ContentType
|
||||
{
|
||||
UNKNOWN,
|
||||
MOD,
|
||||
MODPACK,
|
||||
GAME,
|
||||
TXP
|
||||
};
|
||||
|
||||
|
||||
struct ContentSpec
|
||||
{
|
||||
std::string type;
|
||||
@ -37,6 +47,9 @@ struct ContentSpec
|
||||
/// Short description
|
||||
std::string desc;
|
||||
std::string path;
|
||||
std::string textdomain;
|
||||
};
|
||||
|
||||
|
||||
ContentType getContentType(const std::string &path);
|
||||
void parseContentInfo(ContentSpec &spec);
|
||||
|
@ -36,6 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "client/guiscalingfilter.h"
|
||||
#include "irrlicht_changes/static_text.h"
|
||||
#include "client/tile.h"
|
||||
#include "content/content.h"
|
||||
#include "content/mods.h"
|
||||
|
||||
#if USE_SOUND
|
||||
#include "client/sound/sound_openal.h"
|
||||
@ -204,6 +206,57 @@ GUIEngine::GUIEngine(JoystickController *joystick,
|
||||
m_menu.reset();
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
std::string findLocaleFileInMods(const std::string &path, const std::string &filename)
|
||||
{
|
||||
std::vector<ModSpec> mods = flattenMods(getModsInPath(path, "root", true));
|
||||
|
||||
for (const auto &mod : mods) {
|
||||
std::string ret = mod.path + DIR_DELIM "locale" DIR_DELIM + filename;
|
||||
if (fs::PathExists(ret)) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
Translations *GUIEngine::getContentTranslations(const std::string &path,
|
||||
const std::string &domain, const std::string &lang_code)
|
||||
{
|
||||
if (domain.empty() || lang_code.empty())
|
||||
return nullptr;
|
||||
|
||||
std::string filename = domain + "." + lang_code + ".tr";
|
||||
std::string key = path + DIR_DELIM "locale" DIR_DELIM + filename;
|
||||
|
||||
if (key == m_last_translations_key)
|
||||
return &m_last_translations;
|
||||
|
||||
std::string trans_path = key;
|
||||
ContentType type = getContentType(path);
|
||||
if (type == ContentType::GAME)
|
||||
trans_path = findLocaleFileInMods(path + DIR_DELIM "mods" DIR_DELIM, filename);
|
||||
else if (type == ContentType::MODPACK)
|
||||
trans_path = findLocaleFileInMods(path, filename);
|
||||
// We don't need to search for locale files in a mod, as there's only one `locale` folder.
|
||||
|
||||
if (trans_path.empty())
|
||||
return nullptr;
|
||||
|
||||
m_last_translations_key = key;
|
||||
m_last_translations = {};
|
||||
|
||||
std::string data;
|
||||
if (fs::ReadFile(trans_path, data)) {
|
||||
m_last_translations.loadTranslation(data);
|
||||
}
|
||||
|
||||
return &m_last_translations;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
bool GUIEngine::loadMainMenuScript()
|
||||
{
|
||||
|
@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "client/sound.h"
|
||||
#include "client/tile.h"
|
||||
#include "util/enriched_string.h"
|
||||
#include "translation.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* Structs and macros */
|
||||
@ -165,7 +166,22 @@ public:
|
||||
return m_scriptdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get translations for content
|
||||
*
|
||||
* Only loads a single textdomain from the path, as specified by `domain`,
|
||||
* for performance reasons.
|
||||
*
|
||||
* WARNING: Do not store the returned pointer for long as the contents may
|
||||
* change with the next call to `getContentTranslations`.
|
||||
* */
|
||||
Translations *getContentTranslations(const std::string &path,
|
||||
const std::string &domain, const std::string &lang_code);
|
||||
|
||||
private:
|
||||
std::string m_last_translations_key;
|
||||
/** Only the most recently used translation set is kept loaded */
|
||||
Translations m_last_translations;
|
||||
|
||||
/** find and run the main menu script */
|
||||
bool loadMainMenuScript();
|
||||
|
@ -363,6 +363,9 @@ int ModApiMainMenu::l_get_content_info(lua_State *L)
|
||||
lua_pushstring(L, spec.name.c_str());
|
||||
lua_setfield(L, -2, "name");
|
||||
|
||||
lua_pushstring(L, spec.title.c_str());
|
||||
lua_setfield(L, -2, "title");
|
||||
|
||||
lua_pushstring(L, spec.type.c_str());
|
||||
lua_setfield(L, -2, "type");
|
||||
|
||||
@ -383,6 +386,9 @@ int ModApiMainMenu::l_get_content_info(lua_State *L)
|
||||
lua_pushstring(L, spec.path.c_str());
|
||||
lua_setfield(L, -2, "path");
|
||||
|
||||
lua_pushstring(L, spec.textdomain.c_str());
|
||||
lua_setfield(L, -2, "textdomain");
|
||||
|
||||
if (spec.type == "mod") {
|
||||
ModSpec spec;
|
||||
spec.path = path;
|
||||
@ -432,8 +438,7 @@ int ModApiMainMenu::l_check_mod_configuration(lua_State *L)
|
||||
// Ignore non-string keys
|
||||
if (lua_type(L, -2) != LUA_TSTRING) {
|
||||
throw LuaError(
|
||||
"Unexpected non-string key in table passed to "
|
||||
"core.check_mod_configuration");
|
||||
"Unexpected non-string key in table passed to core.check_mod_configuration");
|
||||
}
|
||||
|
||||
std::string modpath = luaL_checkstring(L, -1);
|
||||
@ -472,7 +477,6 @@ int ModApiMainMenu::l_check_mod_configuration(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushboolean(L, modmgr.isConsistent());
|
||||
@ -500,7 +504,25 @@ int ModApiMainMenu::l_check_mod_configuration(lua_State *L)
|
||||
index++;
|
||||
}
|
||||
lua_setfield(L, -2, "satisfied_mods");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
int ModApiMainMenu::l_get_content_translation(lua_State *L)
|
||||
{
|
||||
GUIEngine* engine = getGuiEngine(L);
|
||||
sanity_check(engine != NULL);
|
||||
|
||||
std::string path = luaL_checkstring(L, 1);
|
||||
std::string domain = luaL_checkstring(L, 2);
|
||||
std::string string = luaL_checkstring(L, 3);
|
||||
std::string lang = gettext("LANG_CODE");
|
||||
if (lang == "LANG_CODE")
|
||||
lang = "";
|
||||
|
||||
auto *translations = engine->getContentTranslations(path, domain, lang);
|
||||
string = wide_to_utf8(translate_string(utf8_to_wide(string), translations));
|
||||
lua_pushstring(L, string.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1102,6 +1124,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
|
||||
API_FCT(get_games);
|
||||
API_FCT(get_content_info);
|
||||
API_FCT(check_mod_configuration);
|
||||
API_FCT(get_content_translation);
|
||||
API_FCT(start);
|
||||
API_FCT(close);
|
||||
API_FCT(show_keys_menu);
|
||||
|
@ -84,6 +84,8 @@ private:
|
||||
|
||||
static int l_check_mod_configuration(lua_State *L);
|
||||
|
||||
static int l_get_content_translation(lua_State *L);
|
||||
|
||||
//gui
|
||||
|
||||
static int l_show_keys_menu(lua_State *L);
|
||||
|
@ -54,6 +54,7 @@ const std::wstring &Translations::getTranslation(
|
||||
void Translations::loadTranslation(const std::string &data)
|
||||
{
|
||||
std::istringstream is(data);
|
||||
std::string textdomain_narrow;
|
||||
std::wstring textdomain;
|
||||
std::string line;
|
||||
|
||||
@ -70,7 +71,8 @@ void Translations::loadTranslation(const std::string &data)
|
||||
<< "\"" << std::endl;
|
||||
continue;
|
||||
}
|
||||
textdomain = utf8_to_wide(trim(parts[1]));
|
||||
textdomain_narrow = trim(parts[1]);
|
||||
textdomain = utf8_to_wide(textdomain_narrow);
|
||||
}
|
||||
if (line.empty() || line[0] == '#')
|
||||
continue;
|
||||
@ -116,7 +118,7 @@ void Translations::loadTranslation(const std::string &data)
|
||||
|
||||
if (i == wline.length()) {
|
||||
errorstream << "Malformed translation line \"" << line << "\""
|
||||
<< std::endl;
|
||||
<< " in text domain " << textdomain_narrow << std::endl;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
|
Loading…
Reference in New Issue
Block a user