3

我一直在尝试实现一种使我的程序双语的方法:用户可以选择程序应该显示法语还是英语(在我的情况下)。我做了很多研究和谷歌搜索,但我仍然找不到一个很好的例子来说明如何做到这一点:/

我阅读了有关 gettext 的信息,但由于这是针对学校的项目,我们不允许使用外部库(我必须承认,即使我尝试过,我也不知道如何使它工作!)

有人还建议我为每种语言使用一个数组,我绝对可以完成这项工作,但我发现解决方案非常难看。

我想到的另一种方法是必须拥有不同的文件,每行都有句子,我可以在需要时检索正确的语言的正确行。我想我可以完成这项工作,但它似乎也不是最优雅的解决方案。

最后,一位朋友说我可以使用DLL。我已经对此进行了调查,它确实似乎是我能找到的最好方法之一......问题是我能找到的关于这件事的大多数资源都是为 C# 和 C++ 编码的,我仍然不知道我会怎么做在 C 中实现:/ 我可以掌握它背后的想法,但不知道如何在 C 中处理它(完全不知道!我不知道如何创建 DLL,调用它,从中检索正确的东西或任何东西>_<)

有人可以指出一些我可以使用的有用资源,或者编写一段代码来解释事情的工作方式或应该完成的方式吗?这将是非常棒的!

非常感谢提前!

(顺便说一句,我使用 Visual Studio 2012 和 C 代码)^^

4

3 回答 3

2

如果您不能使用第三方库,请编写自己的库!不需要dll。

基本思想是每个语言环境都有一个文件,其中包含文本资源的映射(键=值)。

文件名可能类似于

resources_<locale>.txt

哪里<locale>可能是en,frde

当您的程序启动时,它首先读取指定语言环境的资源文件。

最好将每个键/值对存储在一个简单的struct.

您的读取函数将所有键/值对读取到哈希表中,这提供了非常好的访问速度。另一种方法是对包含键/值对的数组进行排序key,然后在查找时使用二进制搜索(不是最佳选择,但比每次迭代所有条目要好得多)。

然后,您必须编写一个函数get_text,将要查找的文本资源的键作为参数,并返回为指定语言环境读取的相应文本。你必须处理没有映射的键,最简单的方法是返回键。

这是一些示例代码(使用qsortbsearch):

#include<stdio.h> 
#include<stdlib.h>
#include<string.h>

#define DEFAULT_LOCALE "en"
#define NULL_ARG "[NULL]"

typedef struct localized_text {
    char* key;
    char* value;
} localized_text_t;

localized_text_t* localized_text_resources = NULL;
int counter = 0;

char* get_text(char*);
void read_localized_text_resources(char*);
char* read_line(FILE*);
void free_localized_text_resources();
int compare_keys(const void*, const void*);
void print_localized_text_resources();


int main(int argc, char** argv)  
{
    argv++;
    argc--;

    char* locale = DEFAULT_LOCALE;

    if(! *argv) {
        printf("No locale provided, default to %s\n", locale);
    } else {
        locale = *argv;
        printf("Locale provided is %s\n", locale);
    }

    read_localized_text_resources(locale);

    printf("\n%s, %s!\n", get_text("HELLO"), get_text("WORLD"));
    printf("\n%s\n", get_text("foo"));

    free_localized_text_resources();

    return 0;  
} 


char* get_text(char* key)
{
    char* text = NULL_ARG;
    if(key) {
        text = key;
        localized_text_t tmp;
        tmp.key = key;
        localized_text_t* result = bsearch(&tmp, localized_text_resources, counter, sizeof(localized_text_t), compare_keys);
        if(result) {
            text = result->value;
        }
    }    
    return text;
}

void read_localized_text_resources(char* locale)
{
    if(locale) {
        char localized_text_resources_file_name[64];
        sprintf(localized_text_resources_file_name, "resources_%s.txt", locale);
        printf("Read localized text resources from file %s\n", localized_text_resources_file_name);
        FILE* localized_text_resources_file = fopen(localized_text_resources_file_name, "r");
        if(! localized_text_resources_file) {
            perror(localized_text_resources_file_name);
            exit(1);
        }
        int size = 10;
        localized_text_resources = malloc(size * sizeof(localized_text_t));
        if(! localized_text_resources) {
            perror("Unable to allocate memory for text resources");
        }

        char* line;
        while((line = read_line(localized_text_resources_file))) {
            if(strlen(line) > 0) {
                if(counter == size) {
                    size += 10;
                    localized_text_resources = realloc(localized_text_resources, size * sizeof(localized_text_t));
                }
                localized_text_resources[counter].key = line;
                while(*line != '=') {
                    line++;
                }
                *line = '\0';
                line++;
                localized_text_resources[counter].value = line;
                counter++;
            }
        }
        qsort(localized_text_resources, counter, sizeof(localized_text_t), compare_keys);
        // print_localized_text_resources();
        printf("%d text resource(s) found in file %s\n", counter, localized_text_resources_file_name);
    }
}


char* read_line(FILE* p_file)
{
    int len = 10, i = 0, c = 0;
    char* line = NULL;

    if(p_file) {
        line = malloc(len * sizeof(char));
        c = fgetc(p_file);
        while(c != EOF) {
            if(i == len) {
                len += 10;
                line = realloc(line, len * sizeof(char));
            }
            line[i++] = c;
            c = fgetc(p_file);
            if(c == '\n' || c == '\r') {
                break;
            }
        }

        line[i] = '\0';

        while(c == '\n' || c == '\r') {
            c = fgetc(p_file);
        }
        if(c != EOF) {
            ungetc(c, p_file);
        }

        if(strlen(line) == 0 && c == EOF) {
            free(line);
            line = NULL;
        }
    }

    return line;
}


void free_localized_text_resources()
{
    if(localized_text_resources) {
        while(counter--) {
            free(localized_text_resources[counter].key);
        }
        free(localized_text_resources);
    }
}


int compare_keys(const void* e1, const void* e2)
{
    return strcmp(((localized_text_t*) e1)->key, ((localized_text_t*) e2)->key);
}


void print_localized_text_resources() 
{
    int i = 0;
    for(; i < counter; i++) {
        printf("Key=%s  value=%s\n", localized_text_resources[i].key, localized_text_resources[i].value);
    }
}

与以下资源文件一起使用

resources_en.txt

WORLD=World
HELLO=Hello

resources_de.txt

HELLO=Hallo
WORLD=Welt

resources_fr.txt

HELLO=Hello
WORLD=Monde

(1) out.exe     /* default */
(2) out.exe en
(3) out.exe de
(4) out.exe fr

输出

(1) Hello, World!
(2) Hello, World!
(3) Hallo, Welt!
(4) Hello, Monde!
于 2013-04-24T23:00:30.533 回答
0

gettext 是显而易见的答案,但在您的情况下似乎不可能。嗯。如果你真的,真的需要一个定制的解决方案......在这里抛出一个疯狂的想法......

1:创建自定义多语言字符串类型。好处是您可以在之后轻松添加新语言,如果您愿意的话。您将在 #4 中看到的缺点。

//Terrible name, change it
typedef struct
{
    char *french;
    char *english;
} MyString; 

2:根据需要定义您的字符串。

MyString s;
s.french = "Bonjour!";
s.english = "Hello!";

3:实用枚举和函数

enum
{
    ENGLISH,
    FRENCH
};

char* getLanguageString(MyString *myStr, int language)
{
    switch(language)
    {
        case ENGLISH:
            return myStr->english;
            break;
        case FRENCH:
            return myStr->french;
            break;
        default:
            //How you handle other values is up to you. You could decide on a default, for instance
            //TODO
    }
}

4:创建包装函数而不是使用普通的旧 C 标准函数。例如,而不是printf

//Function should use the variable arguments and allow a custom format, too
int myPrintf(const char *format, MyString *myStr, int language, ...)
{
    return printf(format, getLanguageString(myStr, language));
}

那部分是痛苦的:您需要覆盖使用字符串的每个函数来处理自定义字符串。您还可以指定一个全局默认语言变量以在未指定时使用。

再说一遍:gettext 好多了。仅在您确实需要时才执行此操作。

于 2013-04-24T18:58:45.320 回答
0

使程序可翻译的主要思想是在所有使用文本的地方使用任何类型的 id。然后在显示测试之前,您可以使用相应语言表的 id 获取文本。

例子:

而不是写

printf("%s","Hello world");

你写

printf("%s",myGetText(HELLO_WORLD)); 

通常使用本地语言字符串本身而不是 id。例如:

printf("%s",myGetText("Hello world"));

最后,myGetText 函数通常实现为宏,例如:

printf("%s", tr("Hello world")); 

外部解析器(如在 gettext 中)可以使用此宏来识别要在源代码中翻译的文本并将它们作为列表存储在文件中。

myGetText 可以按如下方式实现:

std::map<std::string, std::map<std::string, std::string> > LangTextTab;
std::string GlobalVarLang="en"; //change to de for obtaining texts in German

void readLanguagesFromFile()
{

  LangTextTab["de"]["Hello"]="Hallo";
  LangTextTab["de"]["Bye"]="Auf Wiedersehen";
  LangTextTab["en"]["Hello"]="Hello";
  LangTextTab["en"]["Bye"]="Bye";
}

const char * myGetText( const char* origText )
{
  return LangTextTab[GlobalVarLang][origText ].c_str();
}

请将该代码视为伪代码。我没有编译它。许多问题仍然需要提及:unicode、线程安全等......但是我希望这个例子能给你如何开始的想法。

于 2013-04-24T19:16:30.170 回答