2013-04-24 141 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