There is a problem with Russian localization - it shows door's destionation and, sometimes, current area is English (see screenshot1, screenshot2).
That happens because in Russian version some cell names (location cells as far as i can see) are not translated in the ESM files.
Instead, there are special *.cel files (one for each ESM - morrowind.cel, tribunal.cel and bloodmoon.cel), which contain the translations for all the untranslated cell names.
I dont know why they did so, but fact is fact, so OpenMW need to show that translations. I mean, it can keep the original titles for internal purpoises, but a user should see the translated onse.
A *.cel file has very primitive format: many rows, each row has an original title, the tab symbol and the translation. Here is the example:
The files are encoded in windows1251 (for the Russian version).Holamayan Холамаян
Sadrith Mora Садрит Мора
Wolverine Hall Волверин Холл
Nchurdamz Нчурдамц
Tel Aruhn Тель Арун
Tel Fyr Тель Фир
It is very easy to implement that feachure, and i did it (for the door tooltips) on my local sources, but, because OpenMW sources are new for me, i need an advice how to harmoniously add my code.
I did the following:
1. Added some data and methods to MWWorld::ESMStore (because there is one cell translation file per esm file, i guess it is right place):
Code: Select all
namespace MWWorld
{
<...>
class ESMStore
{
<...>
std::map<std::string, std::string> mCellNamesTranslations;
void loadCellNamesTranslation(const std::string& path, ToUTF8::FromType encoding);
std::string translateCellName(const std::string& cellName) const;
<...>
}
<...>
}
Code: Select all
void ESMStore::loadCellNamesTranslation(const std::string& path, ToUTF8::FromType encoding)
{
std::ifstream stream(path);
if (stream.is_open())
{
std::string line;
while (!stream.eof())
{
std::getline( stream, line );
if (!line.empty())
{
char* buffer = ToUTF8::getBuffer(line.size() + 1);
//buffer has at least line.size() + 1 bytes, so it must be safe
strcpy(buffer, line.c_str());
line = ToUTF8::getUtf8(encoding);
size_t tab_pos = line.find('\t');
if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1)
{
std::string original = line.substr(0, tab_pos);
std::string translation = line.substr(tab_pos + 1);
if (!original.empty() && !translation.empty())
mCellNamesTranslations.insert(std::make_pair(original, translation));
}
}
}
stream.close();
}
}
std::string ESMStore::translateCellName(const std::string& cellName) const
{
std::map<std::string, std::string>::const_iterator entry = mCellNamesTranslations.find(cellName);
if (entry == mCellNamesTranslations.end())
return cellName;
return entry->second;
}
The *.cell file use \r\n line endings, but on my linux computer std::getline not treating \r as a part of line ending sequence.
Is there any good way to read lines correctly except to remove \r manually?
Problem2:
There are more surprizes from Bethesda dev/translation team: not only *.cel files, but also, for example, *.top files (see https://bugs.openmw.org/issues/253) and *.mrk files. All those files have similar structure, so
maybe it is good to write a parser class or function for them in components/ folder?
Here is the way I load the cell translation file:
Code: Select all
World::World (OEngine::Render::OgreRenderer& renderer,
const Files::Collections& fileCollections,
const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
const std::string& encoding, std::map<std::string,std::string> fallbackMap)
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
mSky (true), mCells (mStore, mEsm),
mNumFacing(0)
{
<...>
std::cout << "Loading ESM " << masterPath.string() << "\n";
// This parses the ESM file and loads a sample cell
mEsm.setEncoding(encoding);
mEsm.open (masterPath.string());
mStore.load (mEsm);
//Loading sell name translations from a file wich have the same name with the master file
//(but with .cel extension and in lowercase).
//I guess this section should be modified when multi-esm support will be done
{
std::string cellTranslationFileName = StringUtils::lowerCase(master);
//changing the extension
size_t dotPos = cellTranslationFileName.rfind('.');
if (dotPos != std::string::npos)
cellTranslationFileName.replace(dotPos, cellTranslationFileName.length() - dotPos, ".cel");
std::string cellTranslationFilePath = fileCollections.getCollection(".cel").getPath(cellTranslationFileName).string();
ToUTF8::FromType ft;
if (encoding == "win1250")
ft= ToUTF8::WINDOWS_1250;
else if (encoding == "win1251")
ft = ToUTF8::WINDOWS_1251;
else
ft = ToUTF8::WINDOWS_1252;
mStore.loadCellNamesTranslation(cellTranslationFilePath, ft);
}
<...>
}
To read the file correctly the loadCellNamesTranslation method need to know the file encoding (It is always win1251 for Russian files but, ofc, can differ in other versions). World::World() takes encoding as a string, but it need to be converted to ToUTF8::FromType. I have seen at least two places in the code where such conversions made - it is unnecessary to copy this code.
Maybe the conversion should be made once at the program start, and then the program should use ToUTF8::FromType everywhere?
Problem 4:
From my point of view, it is bad design to pass encoding into the loadCellNamesTranslation method. Decoding is not his task. So if we will solve problem 2 by creating a special parser, we can pass encoding to it, and pass the parser
to the the loadCellNamesTranslation.
Problem 5:
There are many places where (untranslated) cell names are shown to the user. Door tooltips, map point tooltips, location title above minimap.... At that moment I found only the place where door tooltips are shown.
Could you point me to other places in the code?