2013-04-24 129 views
3

我一直在试图实现一种让我的程序成为双语的方式:用户可以选择程序是显示法语还是英语(在我的情况下)。 我做了大量的研究和谷歌搜索,但我仍然无法找到一个很好的例子如何做到这一点:/控制台应用程序中的双语程序C

我读了gettext,但由于这是一个学校的项目,我们不允许使用外部库(我必须承认我没有想法,即使我尝试过如何让它工作!)

有人还建议我使用每种语言的数组一个,我可以肯定地做这个工作,但我觉得解决方案超级丑。

我想到的另一种方式是必须使用不同的文件,并在每行上添加句子,并且在需要时我能够检索正确语言的正确行。我想我可以完成这项工作,但它似乎也不是最优雅的解决方案。

最后,一位朋友说我可以使用DLL。我已经看到了这一点,它似乎确实是我能找到的最好的方法之一......问题是我能找到的大部分资源都是为C#和C++编写的,而且我仍然不知道该怎么做在C:/ 中实现我可以理解它背后的想法,但不知道如何在C中处理它(根本不知道如何创建DLL,调用它,从中检索正确的东西,或者任何东西> _ <)

有人可以指点我可以使用的一些有用资源,或者写一段代码来解释工作方式或应该完成的方式吗? 这将是严重的真棒!

非常感谢!

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

+2

使用gettext。这很简单。 – 2013-04-24 18:51:40

回答

2

如果你不能使用第三方库,然后写你自己的!不需要一个dll。

的基本思想是为每个locale女巫一个文件包含文本资源的映射(键=值)。

该文件的名称可能是像

resources_<locale>.txt 

其中<locale>可以像enfrde

当你的程序星星它首先读取指定区域的资源文件。

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

你读函数读取所有的键/值对成hash table女巫提供了一个很好的访问速度。另一种方法是通过key到含有键/值对数组进行排序,然后在查找(不是最佳的选择,但远远低于遍历每次所有条目更好)使用binary search

然后,你将不得不写一个函数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! 
+0

这是一个非常酷的解决方案! :D谢谢! 我不得不为一些东西修改一些代码(VS在抱怨一些失败的东西,但没有太坏)。 虽然快速的问题:如果我将它编译为.cpp文件,它工作得很好,如果我将扩展名更改为.c,它开始抱怨一堆未定义的变量? 我将代码拆分为.h文件和.cpp文件:然后开始提供LINK2005错误,并说已经定义了localized_text_resources和counter。将它们从标题中移除并放置在.cpp文件顶部似乎可以修复它,但是为什么? ^^ – 2013-04-25 13:47:23

+0

不客气!代码是用'GNU'' gcc'编译器(C编译器不是g ++ witch就是C++编译器)编译成一个'.c'文件。关于转换警告,如果你用'C++'编译器编译包含'malloc'调用的代码,那么你需要将它转换为目标类型,如果使用'C'编译器编译代码,这不是强制性的。关于未定义的变量,我想不出为什么因为所有变量被定义为全局变量,并且在任何使用之前,MS倾向于制定他们自己的标准:/。您需要查看VS c编译器的文档。 – A4L 2013-04-25 18:27:51

+0

关于Link2005,也许你已经在两个文件O_o中。将扩展名从'.c'改为'.cpp'可让IDE(您的VS)自动选择合适的编译器。所以有一次你使用C编译器和C规则编译,另一次使用C++编译器和C++规则编译。 – A4L 2013-04-25 18:30:11

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好多了,好多了。只有在你确实需要的时候才能实现。

+0

这可以做得更简单。例如,我的一些旧CGI使用[宏](http://eddy-em.livejournal.com/5319.html)(俄文文本!)。 – 2013-04-24 19:03:00

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的)将被翻译在源代码中并将它们作为列表存储在文件中。

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(); 
} 

请考虑代码为伪代码:

如下myGetText无法实施。我没有编译它。许多问题还有待提及:unicode,线程安全等... 但是,我希望这个例子会给你如何开始的想法。

+0

你的想法是'gettext'的一个非常缓慢的变体。 Gettext更好,因为它将本地化​​存储在类似BD的文件中,所以为了找到一些文本,它不需要任何时间扫描所有字符串,而是通过哈希获取它需要的字符串。每次运行'myGetText'时,您的变体将扫描所有翻译。 – 2013-04-24 19:59:38

+0

对我来说似乎它可能是一个很好的方式来做到这一点,但我认为你的代码是用C++编写的,我对它的语法并不熟悉,所以我不知道那里发生了什么,例如: std :: map < std :: string,std :: map > LangTextTab; std :: string GlobalVarLang =“en”; – 2013-04-25 13:49:09

+0

@ZoédeMoffarts地图是C++标准库中的一个键值映射容器。除其他外(例如,提供快速键搜索,因为它是红黑树,而不是列表:),它还提供了下标运算符 - 它看起来像数组,但对于任何索引类型不仅数字。我的意图不是为了向你提供代码,而是为了解释这个想法。看起来,就像你现在知道的那样。 P.S:在提供C++编译器的系统上看不到使用C的许多原因。看看我提供的例子和A4L的例子。他们与完全不同的LOC完全相同:-) – 2013-04-25 20:44:10