TRANSLATION: added ScummVM translation files with updates

This commit is contained in:
Pawel Kolodziejski 2015-08-26 22:06:26 +02:00
parent fd6f4ad7a0
commit 787b3f4195
31 changed files with 72431 additions and 6 deletions

View File

@ -0,0 +1,106 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* This is a utility for create the translations.dat file from all the po files.
* The generated files is used by ScummVM to propose translation of its GUI.
*/
#include "cp_parser.h"
#include <fstream>
#include <stdio.h>
Codepage::Codepage(const std::string &name, const uint32 *mapping) : _name(name) {
if (!mapping) {
// Default to a ISO-8859-1 mapping
for (unsigned int i = 0; i < 256; ++i)
_mapping[i] = i;
} else {
for (unsigned int i = 0; i < 256; ++i)
_mapping[i] = *mapping++;
}
}
Codepage *parseCodepageMapping(const std::string &filename) {
size_t start = filename.find_last_of("/\\");
if (start == std::string::npos)
start = 0;
else
++start;
// Strip off the filename extension
const size_t pos = filename.find_last_of('.');
const std::string charset(filename.substr(start, pos != std::string::npos ? (pos - start) : std::string::npos));
std::ifstream in(filename.c_str());
if (!in) {
fprintf(stderr, "ERROR: Could not open file \"%s\"!", filename.c_str());
return 0;
}
uint32 mapping[256];
int processedMappings = 0;
std::string line;
while (processedMappings < 256) {
std::getline(in, line);
if (in.eof())
break;
if (in.fail()) {
fprintf(stderr, "ERROR: Reading from file \"%s\" failed!", filename.c_str());
return 0;
}
// In case the line starts with a "#" it is a comment. We also ignore
// empty lines.
if (line.empty() || line[0] == '#')
continue;
// Read the unicode number, we thereby ignore everything else on the line
int unicode, required;
const int parsed = sscanf(line.c_str(), "%d %d", &unicode, &required);
if (parsed < 1 || parsed > 2) {
fprintf(stderr, "ERROR: Line \"%s\" is malformed!", filename.c_str());
return 0;
}
// In case no required integer was given, we assume the character is
// required
if (parsed == 1)
required = 1;
// -1 means default mapping
if (unicode == -1)
unicode = processedMappings;
uint32 unicodeValue = unicode;
if (required)
unicodeValue |= 0x80000000;
mapping[processedMappings++] = (uint32)unicodeValue;
}
// Check whether all character encodings are mapped, if not error out
if (processedMappings != 256) {
fprintf(stderr, "ERROR: File \"%s\" does not contain mappings for exactly 256 characters!", filename.c_str());
return 0;
}
// Return the codepage
return new Codepage(charset, mapping);
}

View File

@ -0,0 +1,54 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* This is a utility for create the translations.dat file from all the po files.
* The generated files is used by ScummVM to propose translation of its GUI.
*/
#ifndef CP_PARSER_H
#define CP_PARSER_H
#include "create_translations.h"
#include <string>
/**
* Codepage description.
*
* This includes a name, and the codepage -> unicode mapping.
*/
class Codepage {
public:
Codepage(const std::string &name, const uint32 *mapping);
const std::string &getName() const { return _name; }
uint32 getMapping(unsigned char src) const { return _mapping[src]; }
private:
std::string _name;
uint32 _mapping[256];
};
/**
* Parse the codepage file and create a codepage.
*/
Codepage *parseCodepageMapping(const std::string &filename);
#endif

View File

@ -0,0 +1,273 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* This is a utility for create the translations.dat file from all the po files.
* The generated files is used by ScummVM to propose translation of its GUI.
*/
#include "create_translations.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <vector>
// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
#ifdef main
#undef main
#endif // main
#include "po_parser.h"
#include "cp_parser.h"
#define TRANSLATIONS_DAT_VER 3 // 1 byte
// Portable implementation of stricmp / strcasecmp / strcmpi.
int scumm_stricmp(const char *s1, const char *s2) {
uint8 l1, l2;
do {
// Don't use ++ inside tolower, in case the macro uses its
// arguments more than once.
l1 = (uint8)*s1++;
l1 = tolower(l1);
l2 = (uint8)*s2++;
l2 = tolower(l2);
} while (l1 == l2 && l1 != 0);
return l1 - l2;
}
// Padding buffer (filled with 0) used if we want to aligned writes
// static uint8 padBuf[DATAALIGNMENT];
// Utility functions
// Some of the function are very simple but are factored out so that it would require
// minor modifications if we want for example to aligne writes on 4 bytes.
void writeByte(FILE *fp, uint8 b) {
fwrite(&b, 1, 1, fp);
}
void writeUint16BE(FILE *fp, uint16 value) {
writeByte(fp, (uint8)(value >> 8));
writeByte(fp, (uint8)(value & 0xFF));
}
void writeUint32BE(FILE *fp, uint32 value) {
writeByte(fp, (uint8)(value >> 24));
writeByte(fp, (uint8)(value >> 16));
writeByte(fp, (uint8)(value >> 8));
writeByte(fp, (uint8)(value & 0xFF));
}
int stringSize(const char *string) {
// Each string is preceded by its size coded on 2 bytes
if (string == NULL)
return 2;
int len = strlen(string) + 1;
return 2 + len;
// The two lines below are an example if we want to align string writes
// pad = DATAALIGNMENT - (len + 2) % DATAALIGNMENT;
// return 2 + len + pad;
}
void writeString(FILE *fp, const char *string) {
// Each string is preceded by its size coded on 2 bytes
if (string == NULL) {
writeUint16BE(fp, 0);
return;
}
int len = strlen(string) + 1;
writeUint16BE(fp, len);
fwrite(string, len, 1, fp);
// The commented lines below are an example if we want to align string writes
// It replaces the two lines above.
// int pad = DATAALIGNMENT - (len + 2) % DATAALIGNMENT;
// writeUint16BE(fp, len + pad);
// fwrite(string, len, 1, fp);
// fwrite(padBuf, pad, 1, fp);
}
// Main
int main(int argc, char *argv[]) {
std::vector<Codepage *> codepages;
// Add default codepages, we won't store them in the output later on
codepages.push_back(new Codepage("ascii", 0));
codepages.push_back(new Codepage("iso-8859-1", 0));
// Build the translation and codepage list
PoMessageList messageIds;
std::vector<PoMessageEntryList *> translations;
int numLangs = 0;
for (int i = 1; i < argc; ++i) {
// Check file extension
int len = strlen(argv[i]);
if (scumm_stricmp(argv[i] + len - 2, "po") == 0) {
PoMessageEntryList *po = parsePoFile(argv[i], messageIds);
if (po != NULL) {
translations.push_back(po);
++numLangs;
}
} else if (scumm_stricmp(argv[i] + len - 2, "cp") == 0) {
// Else try to parse an codepage
Codepage *co = parseCodepageMapping(argv[i]);
if (co)
codepages.push_back(co);
}
}
// Parse all charset mappings
for (int i = 0; i < numLangs; ++i) {
bool found = false;
for (size_t j = 0; j < codepages.size(); ++j) {
if (scumm_stricmp(codepages[j]->getName().c_str(), translations[i]->charset()) == 0) {
found = true;
break;
}
}
// In case the codepage was not found error out
if (!found) {
fprintf(stderr, "ERROR: No codepage mapping for codepage \"%s\" present!\n", translations[i]->charset());
for (size_t j = 0; j < translations.size(); ++j)
delete translations[j];
for (size_t j = 0; j < codepages.size(); ++j)
delete codepages[j];
return -1;
}
}
FILE *outFile;
int i, lang;
int len;
// Padding buffer initialization (filled with 0)
// used if we want to aligned writes
// for (i = 0; i < DATAALIGNMENT; i++)
// padBuf[i] = 0;
outFile = fopen("translations.dat", "wb");
// Write header
fwrite("TRANSLATIONS", 12, 1, outFile);
writeByte(outFile, TRANSLATIONS_DAT_VER);
// Write number of translations
writeUint16BE(outFile, numLangs);
// Write number of codepages, we don't save ascii and iso-8859-1
writeUint16BE(outFile, codepages.size() - 2);
// Write the length of each data block here.
// We could write it at the start of each block but that would mean that
// to go to block 4 we would have to go at the start of each preceding block,
// read its size and skip it until we arrive at the block we want.
// By having all the sizes at the start we just need to read the start of the
// file and can then skip to the block we want.
// Blocks are:
// 1. List of languages with the language name
// 2. List of codepages
// 3. Original messages (i.e. english)
// 4. First translation
// 5. Second translation
// ...
// n. First codepage (These don't have any data size, since they are all
// 256 * 4 bytes long)
// n+1. Second codepage
// ...
// Write length for translation description
len = 0;
for (lang = 0; lang < numLangs; lang++) {
len += stringSize(translations[lang]->language());
len += stringSize(translations[lang]->languageName());
}
writeUint16BE(outFile, len);
// Write length for the codepage names
len = 0;
for (size_t j = 2; j < codepages.size(); ++j)
len += stringSize(codepages[j]->getName().c_str());
writeUint16BE(outFile, len);
// Write size for the original language (english) block
// It starts with the number of strings coded on 2 bytes followed by each
// string (two bytes for the number of chars and the string itself).
len = 2;
for (i = 0; i < messageIds.size(); ++i)
len += stringSize(messageIds[i]);
writeUint16BE(outFile, len);
// Then comes the size of each translation block.
// It starts with the number of strings coded on 2 bytes, the charset and then the strings.
// For each string we have the string id (on two bytes) followed by
// the string size (two bytes for the number of chars and the string itself).
for (lang = 0; lang < numLangs; lang++) {
len = 2 + stringSize(translations[lang]->charset());
for (i = 0; i < translations[lang]->size(); ++i) {
len += 2 + stringSize(translations[lang]->entry(i)->msgstr);
len += stringSize(translations[lang]->entry(i)->msgctxt);
}
writeUint16BE(outFile, len);
}
// Write list of languages
for (lang = 0; lang < numLangs; lang++) {
writeString(outFile, translations[lang]->language());
writeString(outFile, translations[lang]->languageName());
}
// Write list of codepages
for (size_t j = 2; j < codepages.size(); ++j) {
writeString(outFile, codepages[j]->getName().c_str());
}
// Write original messages
writeUint16BE(outFile, messageIds.size());
for (i = 0; i < messageIds.size(); ++i) {
writeString(outFile, messageIds[i]);
}
// Write translations
for (lang = 0; lang < numLangs; lang++) {
writeUint16BE(outFile, translations[lang]->size());
writeString(outFile, translations[lang]->charset());
for (i = 0; i < translations[lang]->size(); ++i) {
writeUint16BE(outFile, messageIds.findIndex(translations[lang]->entry(i)->msgid));
writeString(outFile, translations[lang]->entry(i)->msgstr);
writeString(outFile, translations[lang]->entry(i)->msgctxt);
}
}
// Write codepages
for (size_t j = 2; j < codepages.size(); ++j) {
const Codepage *cp = codepages[j];
for (i = 0; i < 256; ++i)
writeUint32BE(outFile, cp->getMapping(i));
}
fclose(outFile);
// Clean the memory
for (i = 0; i < numLangs; ++i)
delete translations[i];
return 0;
}

View File

@ -0,0 +1,35 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CREATE_TRANSLATIONS_H
#define CREATE_TRANSLATIONS_H
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef signed short int16;
#ifndef __has_feature // Optional of course.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
#endif /* CREATE_TRANSLATIONS_H */

View File

@ -0,0 +1,12 @@
MODULE := devtools/create_translations
MODULE_OBJS := \
cp_parser.o \
po_parser.o \
create_translations.o
# Set the name of the executable
TOOL_EXECUTABLE := create_translations
# Include common rules
include $(srcdir)/rules.mk

View File

@ -0,0 +1,403 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* This is a utility for create the translations.dat file from all the po files.
* The generated files is used by ScummVM to propose translation of its GUI.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "po_parser.h"
PoMessageList::PoMessageList() : _messages(NULL), _size(0), _allocated(0) {
}
PoMessageList::~PoMessageList() {
for (int i = 0; i < _size; ++i)
delete[] _messages[i];
delete[] _messages;
}
void PoMessageList::insert(const char *msg) {
if (msg == NULL || *msg == '\0')
return;
// binary-search for the insertion index
int leftIndex = 0;
int rightIndex = _size - 1;
while (rightIndex >= leftIndex) {
int midIndex = (leftIndex + rightIndex) / 2;
int compareResult = strcmp(msg, _messages[midIndex]);
if (compareResult == 0)
return;
else if (compareResult < 0)
rightIndex = midIndex - 1;
else
leftIndex = midIndex + 1;
}
// We now have rightIndex = leftIndex - 1 and we need to insert the new message
// between the two (i.a. at leftIndex).
if (_size + 1 > _allocated) {
_allocated += 100;
char **newMessages = new char*[_allocated];
for (int i = 0; i < leftIndex; ++i)
newMessages[i] = _messages[i];
for (int i = leftIndex; i < _size; ++i)
newMessages[i + 1] = _messages[i];
delete[] _messages;
_messages = newMessages;
} else {
for (int i = _size - 1; i >= leftIndex; --i)
_messages[i + 1] = _messages[i];
}
_messages[leftIndex] = new char[1 + strlen(msg)];
strcpy(_messages[leftIndex], msg);
++_size;
}
int PoMessageList::findIndex(const char *msg) {
if (msg == NULL || *msg == '\0')
return -1;
// binary-search for the message
int leftIndex = 0;
int rightIndex = _size - 1;
while (rightIndex >= leftIndex) {
const int midIndex = (leftIndex + rightIndex) / 2;
const int compareResult = strcmp(msg, _messages[midIndex]);
if (compareResult == 0)
return midIndex;
else if (compareResult < 0)
rightIndex = midIndex - 1;
else
leftIndex = midIndex + 1;
}
return -1;
}
int PoMessageList::size() const {
return _size;
}
const char *PoMessageList::operator[](int index) const {
if (index < 0 || index >= _size)
return NULL;
return _messages[index];
}
PoMessageEntryList::PoMessageEntryList(const char *lang) :
_lang(NULL), _charset(NULL), _langName(NULL),
_list(NULL), _size(0), _allocated(0)
{
_lang = new char[1 + strlen(lang)];
strcpy(_lang, lang);
// Set default charset to empty string
_charset = new char[1];
_charset[0] = '\0';
// Set default langName to lang
_langName = new char[1 + strlen(lang)];
strcpy(_langName, lang);
}
PoMessageEntryList::~PoMessageEntryList() {
delete[] _lang;
delete[] _charset;
delete[] _langName;
for (int i = 0; i < _size; ++i)
delete _list[i];
delete[] _list;
}
void PoMessageEntryList::addMessageEntry(const char *translation, const char *message, const char *context) {
if (*message == '\0') {
// This is the header.
// We get the charset and the language name from the translation string
char *str = parseLine(translation, "Language:");
if (str != NULL) {
delete[] _langName;
_langName = str;
}
str = parseLine(translation, "charset=");
if (str != NULL) {
delete[] _charset;
_charset = str;
}
return;
}
// binary-search for the insertion index
int leftIndex = 0;
int rightIndex = _size - 1;
while (rightIndex >= leftIndex) {
int midIndex = (leftIndex + rightIndex) / 2;
int compareResult = strcmp(message, _list[midIndex]->msgid);
if (compareResult == 0) {
if (context == NULL) {
if (_list[midIndex]->msgctxt == NULL)
return;
compareResult = -1;
} else {
if (_list[midIndex]->msgctxt == NULL)
compareResult = 1;
else {
compareResult = strcmp(context, _list[midIndex]->msgctxt);
if (compareResult == 0)
return;
}
}
}
if (compareResult < 0)
rightIndex = midIndex - 1;
else
leftIndex = midIndex + 1;
}
// We now have rightIndex = leftIndex - 1 and we need to insert the new message
// between the two (i.a. at leftIndex).
// However since the TranslationManager will pick the translation associated to no
// context if it is not present for a specific context, we can optimize the file
// size, memory used at run-time and performances (less strings to read from the file
// and less strings to look for) by avoiding duplicate.
if (context != NULL && *context != '\0') {
// Check if we have the same translation for no context
int contextIndex = leftIndex - 1;
while (contextIndex >= 0 && strcmp (message, _list[contextIndex]->msgid) == 0) {
--contextIndex;
}
++contextIndex;
if (contextIndex < leftIndex && _list[contextIndex]->msgctxt == NULL && strcmp(translation, _list[contextIndex]->msgstr) == 0)
return;
}
if (_size + 1 > _allocated) {
_allocated += 100;
PoMessageEntry **newList = new PoMessageEntry*[_allocated];
for (int i = 0; i < leftIndex; ++i)
newList[i] = _list[i];
for (int i = leftIndex; i < _size; ++i)
newList[i + 1] = _list[i];
delete[] _list;
_list = newList;
} else {
for (int i = _size - 1; i >= leftIndex; --i)
_list[i + 1] = _list[i];
}
_list[leftIndex] = new PoMessageEntry(translation, message, context);
++_size;
if (context == NULL || *context == '\0') {
// Remove identical translations for a specific context (see comment above)
int contextIndex = leftIndex + 1;
int removed = 0;
while (contextIndex < _size && strcmp(message, _list[contextIndex]->msgid) == 0) {
if (strcmp(translation, _list[contextIndex]->msgstr) == 0) {
delete _list[contextIndex];
++removed;
} else {
_list[contextIndex - removed] = _list[contextIndex];
}
++contextIndex;
}
if (removed > 0) {
while (contextIndex < _size) {
_list[contextIndex - removed] = _list[contextIndex];
++contextIndex;
}
}
_size -= removed;
}
}
const char *PoMessageEntryList::language() const {
return _lang;
}
const char *PoMessageEntryList::languageName() const {
return _langName;
}
const char *PoMessageEntryList::charset() const {
return _charset;
}
int PoMessageEntryList::size() const {
return _size;
}
const PoMessageEntry *PoMessageEntryList::entry(int index) const {
if (index < 0 || index >= _size)
return NULL;
return _list[index];
}
PoMessageEntryList *parsePoFile(const char *file, PoMessageList& messages) {
FILE *inFile = fopen(file, "r");
if (!inFile)
return NULL;
char msgidBuf[1024], msgctxtBuf[1024], msgstrBuf[1024];
char line[1024], *currentBuf = msgstrBuf;
// Get language from file name and create PoMessageEntryList
int index = 0, start_index = strlen(file) - 1;
while (start_index > 0 && file[start_index - 1] != '/' && file[start_index - 1] != '\\') {
--start_index;
}
while (file[start_index + index] != '.' && file[start_index + index] != '\0') {
msgidBuf[index] = file[start_index + index];
++index;
}
msgidBuf[index] = '\0';
PoMessageEntryList *list = new PoMessageEntryList(msgidBuf);
// Initialize the message attributes.
bool fuzzy = false;
bool fuzzy_next = false;
// Parse the file line by line.
// The msgstr is always the last line of an entry (i.e. msgid and msgctxt always
// precede the corresponding msgstr).
msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0';
while (!feof(inFile) && fgets(line, 1024, inFile)) {
if (line[0] == '#' && line[1] == ',') {
// Handle message attributes.
if (strstr(line, "fuzzy")) {
fuzzy_next = true;
continue;
}
}
// Skip empty and comment line
if (*line == '\n' || *line == '#')
continue;
if (strncmp(line, "msgid", 5) == 0) {
if (currentBuf == msgstrBuf) {
// add previous entry
if (*msgstrBuf != '\0' && !fuzzy) {
messages.insert(msgidBuf);
list->addMessageEntry(msgstrBuf, msgidBuf, msgctxtBuf);
}
msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0';
// Reset the attribute flags.
fuzzy = fuzzy_next;
fuzzy_next = false;
}
strcpy(msgidBuf, stripLine(line));
currentBuf = msgidBuf;
} else if (strncmp(line, "msgctxt", 7) == 0) {
if (currentBuf == msgstrBuf) {
// add previous entry
if (*msgstrBuf != '\0' && !fuzzy) {
messages.insert(msgidBuf);
list->addMessageEntry(msgstrBuf, msgidBuf, msgctxtBuf);
}
msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0';
// Reset the attribute flags
fuzzy = fuzzy_next;
fuzzy_next = false;
}
strcpy(msgctxtBuf, stripLine(line));
currentBuf = msgctxtBuf;
} else if (strncmp(line, "msgstr", 6) == 0) {
strcpy(msgstrBuf, stripLine(line));
currentBuf = msgstrBuf;
} else {
// concatenate the string at the end of the current buffer
if (currentBuf)
strcat(currentBuf, stripLine(line));
}
}
if (currentBuf == msgstrBuf) {
// add last entry
if (*msgstrBuf != '\0' && !fuzzy) {
messages.insert(msgidBuf);
list->addMessageEntry(msgstrBuf, msgidBuf, msgctxtBuf);
}
}
fclose(inFile);
return list;
}
char *stripLine(char *const line) {
// This function modifies line in place and return it.
// Keep only the text between the first two unprotected quotes.
// It also look for literal special characters (e.g. preceded by '\n', '\\', '\"', '\'', '\t')
// and replace them by the special character so that strcmp() can match them at run time.
// Look for the first quote
char const *src = line;
while (*src != '\0' && *src++ != '"') {}
// shift characters until we reach the end of the string or an unprotected quote
char *dst = line;
while (*src != '\0' && *src != '"') {
char c = *src++;
if (c == '\\') {
switch (c = *src++) {
case 'n': c = '\n'; break;
case 't': c = '\t'; break;
case '\"': c = '\"'; break;
case '\'': c = '\''; break;
case '\\': c = '\\'; break;
default:
// Just skip
fprintf(stderr, "Unsupported special character \"\\%c\" in string. Please contact ScummVM developers.\n", c);
continue;
}
}
*dst++ = c;
}
*dst = '\0';
return line;
}
char *parseLine(const char *line, const char *field) {
// This function allocate and return a new char*.
// It will return a NULL pointer if the field is not found.
// It is used to parse the header of the po files to find the language name
// and the charset.
const char *str = strstr(line, field);
if (str == NULL)
return NULL;
str += strlen(field);
// Skip spaces
while (*str != '\0' && isspace(*str)) {
++str;
}
// Find string length (stop at the first '\n')
int len = 0;
while (str[len] != '\0' && str[len] != '\n') {
++len;
}
if (len == 0)
return NULL;
// Create result string
char *result = new char[len + 1];
strncpy(result, str, len);
result[len] = '\0';
return result;
}

View File

@ -0,0 +1,110 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef PO_PARSER_H
#define PO_PARSER_H
/**
* List of english messages.
*/
class PoMessageList {
public:
PoMessageList();
~PoMessageList();
void insert(const char *msg);
int findIndex(const char *msg);
int size() const;
const char *operator[](int) const;
private:
char **_messages;
int _size;
int _allocated;
};
/**
* Describes a translation entry.
*/
struct PoMessageEntry {
char *msgstr;
char *msgid;
char *msgctxt;
PoMessageEntry(const char *translation, const char *message, const char *context = NULL) :
msgstr(NULL), msgid(NULL), msgctxt(NULL)
{
if (translation != NULL && *translation != '\0') {
msgstr = new char[1 + strlen(translation)];
strcpy(msgstr, translation);
}
if (message != NULL && *message != '\0') {
msgid = new char[1 + strlen(message)];
strcpy(msgid, message);
}
if (context != NULL && *context != '\0') {
msgctxt = new char[1 + strlen(context)];
strcpy(msgctxt, context);
}
}
~PoMessageEntry() {
delete[] msgstr;
delete[] msgid;
delete[] msgctxt;
}
};
/**
* List of translation entries for one language.
*/
class PoMessageEntryList {
public:
PoMessageEntryList(const char *language);
~PoMessageEntryList();
void addMessageEntry(const char *translation, const char *message, const char *context = NULL);
const char *language() const;
const char *languageName() const;
const char *charset() const;
int size() const;
const PoMessageEntry *entry(int) const;
private:
char *_lang;
char *_charset;
char *_langName;
PoMessageEntry **_list;
int _size;
int _allocated;
};
PoMessageEntryList *parsePoFile(const char *file, PoMessageList &);
char *stripLine(char *);
char *parseLine(const char *line, const char *field);
#endif /* PO_PARSER_H */

View File

@ -2,19 +2,26 @@
gui/about.cpp
gui/browser.cpp
gui/browser_osx.mm
gui/chooser.cpp
gui/error.cpp
gui/editrecorddialog.cpp
gui/fluidsynth-dialog.cpp
gui/gui-manager.cpp
gui/KeysDialog.h
gui/KeysDialog.cpp
gui/launcher.cpp
gui/massadd.cpp
gui/onscreendialog.cpp
gui/options.cpp
gui/recorderdialog.cpp
gui/saveload-dialog.cpp
gui/predictivedialog.cpp
gui/recorderdialog.cpp
gui/saveload-dialog.cpp
gui/themebrowser.cpp
gui/ThemeEngine.cpp
gui/widget.cpp
gui/fluidsynth-dialog.cpp
base/main.cpp
@ -30,11 +37,7 @@ audio/mididrv.cpp
audio/musicplugin.cpp
audio/null.h
audio/null.cpp
audio/softsynth/adlib.cpp
audio/softsynth/appleiigs.cpp
audio/softsynth/sid.cpp
audio/softsynth/mt32.cpp
audio/softsynth/pcspk.cpp
backends/keymapper/remap-dialog.cpp
backends/midi/windows.cpp

3553
po/be_BY.po Normal file

File diff suppressed because it is too large Load Diff

3527
po/ca_ES.po Normal file

File diff suppressed because it is too large Load Diff

3571
po/cs_CZ.po Normal file

File diff suppressed because it is too large Load Diff

3535
po/da_DA.po Normal file

File diff suppressed because it is too large Load Diff

3577
po/de_DE.po Normal file

File diff suppressed because it is too large Load Diff

3538
po/es_ES.po Normal file

File diff suppressed because it is too large Load Diff

3538
po/eu.po Normal file

File diff suppressed because it is too large Load Diff

3512
po/fi_FI.po Normal file

File diff suppressed because it is too large Load Diff

3564
po/fr_FR.po Normal file

File diff suppressed because it is too large Load Diff

3508
po/gl_ES.po Normal file

File diff suppressed because it is too large Load Diff

3537
po/hu_HU.po Normal file

File diff suppressed because it is too large Load Diff

320
po/iso-8859-2.cp Normal file
View File

@ -0,0 +1,320 @@
# 0x00 ( 0)
0 0 # Not required
1 0 # Not required
2 0 # Not required
3 0 # Not required
# 0x04 ( 4)
4 0 # Not required
5 0 # Not required
6 0 # Not required
7 0 # Not required
# 0x08 ( 8)
8 0 # Not required
9 0 # Not required
10 0 # Not required
11 0 # Not required
# 0x0C ( 12)
12 0 # Not required
13 0 # Not required
14 0 # Not required
15 0 # Not required
# 0x10 ( 16)
16 0 # Not required
17 0 # Not required
18 0 # Not required
19 0 # Not required
# 0x14 ( 20)
20 0 # Not required
21 0 # Not required
22 0 # Not required
23 0 # Not required
# 0x18 ( 24)
24 0 # Not required
25 0 # Not required
26 0 # Not required
27 0 # Not required
# 0x1C ( 28)
28 0 # Not required
29 0 # Not required
30 0 # Not required
31 0 # Not required
# 0x20 ( 32)
32
33
34
35
# 0x24 ( 36)
36
37
38
39
# 0x28 ( 40)
40
41
42
43
# 0x2C ( 44)
44
45
46
47
# 0x30 ( 48)
48
49
50
51
# 0x34 ( 52)
52
53
54
55
# 0x38 ( 56)
56
57
58
59
# 0x3C ( 60)
60
61
62
63
# 0x40 ( 64)
64
65
66
67
# 0x44 ( 68)
68
69
70
71
# 0x48 ( 72)
72
73
74
75
# 0x4C ( 76)
76
77
78
79
# 0x50 ( 80)
80
81
82
83
# 0x54 ( 84)
84
85
86
87
# 0x58 ( 88)
88
89
90
91
# 0x5C ( 92)
92
93
94
95
# 0x60 ( 96)
96
97
98
99
# 0x64 (100)
100
101
102
103
# 0x68 (104)
104
105
106
107
# 0x6C (108)
108
109
110
111
# 0x70 (112)
112
113
114
115
# 0x74 (116)
116
117
118
119
# 0x78 (120)
120
121
122
123
# 0x7C (124)
124
125
126
127 0 # Not required
# 0x80 (128)
128 0 # Not required
129 0 # Not required
130 0 # Not required
131 0 # Not required
# 0x84 (132)
132 0 # Not required
133 0 # Not required
134 0 # Not required
135 0 # Not required
# 0x88 (136)
136 0 # Not required
137 0 # Not required
138 0 # Not required
139 0 # Not required
# 0x8C (140)
140 0 # Not required
141 0 # Not required
142 0 # Not required
143 0 # Not required
# 0x90 (144)
144 0 # Not required
145 0 # Not required
146 0 # Not required
147 0 # Not required
# 0x94 (148)
148 0 # Not required
149 0 # Not required
150 0 # Not required
151 0 # Not required
# 0x98 (152)
152 0 # Not required
153 0 # Not required
154 0 # Not required
155 0 # Not required
# 0x9C (156)
156 0 # Not required
157 0 # Not required
158 0 # Not required
159 0 # Not required
# 0xA0 (160)
160
260
728
321
# 0xA4 (164)
164
317
346
167
# 0xA8 (168)
168
352
350
356
# 0xAC (172)
377
173
381
379
# 0xB0 (176)
176
261
731
322
# 0xB4 (180)
180
318
347
711
# 0xB8 (184)
184
353
351
357
# 0xBC (188)
378
733
382
380
# 0xC0 (192)
340
193
194
258
# 0xC4 (196)
196
313
262
199
# 0xC8 (200)
268
201
280
203
# 0xCC (204)
282
205
206
270
# 0xD0 (208)
272
323
327
211
# 0xD4 (212)
212
336
214
215
# 0xD8 (216)
344
366
218
368
# 0xDC (220)
220
221
354
223
# 0xE0 (224)
341
225
226
259
# 0xE4 (228)
228
314
263
231
# 0xE8 (232)
269
233
281
235
# 0xEC (236)
283
237
238
271
# 0xF0 (240)
273
324
328
243
# 0xF4 (244)
244
337
246
247
# 0xF8 (248)
345
367
250
369
# 0xFC (252)
252
253
355
729

320
po/iso-8859-5.cp Normal file
View File

@ -0,0 +1,320 @@
# 0x00 ( 0)
0 0 # Not required
1 0 # Not required
2 0 # Not required
3 0 # Not required
# 0x04 ( 4)
4 0 # Not required
5 0 # Not required
6 0 # Not required
7 0 # Not required
# 0x08 ( 8)
8 0 # Not required
9 0 # Not required
10 0 # Not required
11 0 # Not required
# 0x0C ( 12)
12 0 # Not required
13 0 # Not required
14 0 # Not required
15 0 # Not required
# 0x10 ( 16)
16 0 # Not required
17 0 # Not required
18 0 # Not required
19 0 # Not required
# 0x14 ( 20)
20 0 # Not required
21 0 # Not required
22 0 # Not required
23 0 # Not required
# 0x18 ( 24)
24 0 # Not required
25 0 # Not required
26 0 # Not required
27 0 # Not required
# 0x1C ( 28)
28 0 # Not required
29 0 # Not required
30 0 # Not required
31 0 # Not required
# 0x20 ( 32)
32
33
34
35
# 0x24 ( 36)
36
37
38
39
# 0x28 ( 40)
40
41
42
43
# 0x2C ( 44)
44
45
46
47
# 0x30 ( 48)
48
49
50
51
# 0x34 ( 52)
52
53
54
55
# 0x38 ( 56)
56
57
58
59
# 0x3C ( 60)
60
61
62
63
# 0x40 ( 64)
64
65
66
67
# 0x44 ( 68)
68
69
70
71
# 0x48 ( 72)
72
73
74
75
# 0x4C ( 76)
76
77
78
79
# 0x50 ( 80)
80
81
82
83
# 0x54 ( 84)
84
85
86
87
# 0x58 ( 88)
88
89
90
91
# 0x5C ( 92)
92
93
94
95
# 0x60 ( 96)
96
97
98
99
# 0x64 (100)
100
101
102
103
# 0x68 (104)
104
105
106
107
# 0x6C (108)
108
109
110
111
# 0x70 (112)
112
113
114
115
# 0x74 (116)
116
117
118
119
# 0x78 (120)
120
121
122
123
# 0x7C (124)
124
125
126
127 0 # Not required
# 0x80 (128)
128 0 # Not required
129 0 # Not required
130 0 # Not required
131 0 # Not required
# 0x84 (132)
132 0 # Not required
133 0 # Not required
134 0 # Not required
135 0 # Not required
# 0x88 (136)
136 0 # Not required
137 0 # Not required
138 0 # Not required
139 0 # Not required
# 0x8C (140)
140 0 # Not required
141 0 # Not required
142 0 # Not required
143 0 # Not required
# 0x90 (144)
144 0 # Not required
145 0 # Not required
146 0 # Not required
147 0 # Not required
# 0x94 (148)
148 0 # Not required
149 0 # Not required
150 0 # Not required
151 0 # Not required
# 0x98 (152)
152 0 # Not required
153 0 # Not required
154 0 # Not required
155 0 # Not required
# 0x9C (156)
156 0 # Not required
157 0 # Not required
158 0 # Not required
159 0 # Not required
# 0xA0 (160)
160
1025
1026
1027
# 0xA4 (164)
1028
1029
1030
1031
# 0xA8 (168)
1032
1033
1034
1035
# 0xAC (172)
1036
173
1038
1039
# 0xB0 (176)
1040
1041
1042
1043
# 0xB4 (180)
1044
1045
1046
1047
# 0xB8 (184)
1048
1049
1050
1051
# 0xBC (188)
1052
1053
1054
1055
# 0xC0 (192)
1056
1057
1058
1059
# 0xC4 (196)
1060
1061
1062
1063
# 0xC8 (200)
1064
1065
1066
1067
# 0xCC (204)
1068
1069
1070
1071
# 0xD0 (208)
1072
1073
1074
1075
# 0xD4 (212)
1076
1077
1078
1079
# 0xD8 (216)
1080
1081
1082
1083
# 0xDC (220)
1084
1085
1086
1087
# 0xE0 (224)
1088
1089
1090
1091
# 0xE4 (228)
1092
1093
1094
1095
# 0xE8 (232)
1096
1097
1098
1099
# 0xEC (236)
1100
1101
1102
1103
# 0xF0 (240)
8470
1105
1106
1107
# 0xF4 (244)
1108
1109
1110
1111
# 0xF8 (248)
1112
1113
1114
1115
# 0xFC (252)
1116
167
1118
1119

3555
po/it_IT.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ updatepot:
xgettext -f - -D $(srcdir) -d residualvm --c++ -k_ -k_s -k_c:1,2c -k_sc:1,2c --add-comments=I18N\
-kDECLARE_TRANSLATION_ADDITIONAL_CONTEXT:1,2c -o $(POTFILE) \
--copyright-holder="ResidualVM Team" --package-name=ResidualVM \
--package-version=$(VERSION) --msgid-bugs-address=residualvm-devel@lists.sf.net -o $(POTFILE)_
--package-version=$(VERSION) --msgid-bugs-address=https://github.com/residualvm/residualvm/issues -o $(POTFILE)_
sed -e 's/SOME DESCRIPTIVE TITLE/LANGUAGE translation for ResidualVM/' \
-e 's/UTF-8/CHARSET/' -e 's/PACKAGE/ResidualVM/' $(POTFILE)_ > $(POTFILE).new

3533
po/nb_NO.po Normal file

File diff suppressed because it is too large Load Diff

3502
po/nl_NL.po Normal file

File diff suppressed because it is too large Load Diff

3478
po/nn_NO.po Normal file

File diff suppressed because it is too large Load Diff

3534
po/pl_PL.po Normal file

File diff suppressed because it is too large Load Diff

3589
po/pt_BR.po Normal file

File diff suppressed because it is too large Load Diff

3554
po/ru_RU.po Normal file

File diff suppressed because it is too large Load Diff

3542
po/se_SE.po Normal file

File diff suppressed because it is too large Load Diff

3542
po/uk_UA.po Normal file

File diff suppressed because it is too large Load Diff