2008-10-08 15 views
20

To jest ogólne pytanie, otwarte na opinie. Próbowałem wymyślić dobry sposób projektowania lokalizacji zasobów ciągów dla aplikacji Windows MFC i powiązanych narzędzi. Moja lista życzeń jest:Najlepszy sposób na zaprojektowanie łańcucha znaków

  • musi zachować literały ciągów znaków w kodzie (w przeciwieństwie do zastępując makro #define zasobem identyfikatora), tak, że wiadomości są nadal czytelne inline
  • Musi umożliwić miejscowe zasoby String (duh)
  • nie wolno nakładać dodatkowych ograniczeń środowisko czasu (np: uzależnienie od .NET, itp)
  • powinien mieć minimalną natręctwo do istniejącego kodu (im mniej tym lepiej) modyfikacji
  • Powinny być debuggable
  • powinien generować pliki zasobów, które są edytowane przez zwykłych narzędzi (tj: wspólny format)
  • nie powinno się używać copy/paste komentarz bloki do zachowania dosłownych sznurki w kodzie, lub cokolwiek innego co stwarza potencjał dla de-synchronizacji
  • Would bądź miły, aby umożliwić statyczne (kompilacyjne) sprawdzanie, czy każdy "zapisany" ciąg jest w pliku (plikach) zasobów
  • Byłoby miło pozwolić na łączenie ciągów zasobów w różnych językach (dla komponentów w różnych językach, np .: natywny C++ i .NET)

Mam sposób, który w pewnym stopniu spełnia wszystkie moje życzenia, z wyjątkiem sprawdzania statycznego, ale mam h reklama, aby stworzyć trochę niestandardowego kodu, aby to osiągnąć (i ma ograniczenia). Zastanawiam się, czy ktoś rozwiązał ten problem w szczególnie dobry sposób.

Edit: Rozwiązanie Obecnie mam wygląda następująco:

ShowMessage(RESTRING(_T("Some string"))); 
ShowMessage(RESTRING(_T("Some string with variable %1"), sNonTranslatedStringVariable)); 

Mam następnie niestandardowe narzędzie do analizowania na sznurki od wewnątrz blokuje „restring” i umieścić je w pliku .resx dla lokalizacja i oddzielny obiekt C# COM, aby załadować je z zlokalizowanych plików zasobów z funkcją rezerwową. Jeśli obiekt C# nie jest dostępny (lub nie można go załadować), zastępuję ciąg w kodzie. Makro rozwija się do klasy szablonu, która wywołuje obiekt COM i formatuje, itp.

W każdym razie pomyślałem, że byłoby użyteczne dodanie tego, co mam teraz dla odniesienia.

Odpowiedz

0

W jednym projekcie, który został zlokalizowany w ponad 10 językach, umieściłem wszystko, co miało być zlokalizowane, w pojedynczej bibliotece DLL. W czasie instalacji użytkownik wybrał, która biblioteka dll została zainstalowana wraz z aplikacją.

Musiałem dostarczyć angielską bibliotekę dll do zespołu lokalizacyjnego. Zwrócili mi zlokalizowaną bibliotekę dll dla każdego języka, który zawarłem w kompilacji.

Wiem, że nie jest doskonały, ale zadziałało.

+0

Czy wymyśliłeś sposób, aby to zrobić, nie zastępując ciągów w kodzie identyfikatorem zasobu? To, co opisujesz, brzmi jak potoczna metoda, która z pewnością może zadziałać, ale tak naprawdę nie jest tym, czego szukam. – Nick

+0

Nie, nie tworzymy ciągów kodu w naszych aplikacjach. Widzimy stałą opisową, która odnosi się do identyfikatora zasobu. – BoltBait

1

Nie wiem zbyt wiele na temat tego, jak to zwykle robi się w systemie Windows, ale sposób, w jaki zlokalizowane łańcuchy są obsługiwane w Apple Cocoa framework, działa całkiem nieźle. Mają bardzo prosty plik w formacie tekstowym, który można wysłać do tłumacza i niektóre makra preprocesora, aby pobrać wartości z plików.

W swoim kodzie zobaczysz ciągi w swoim ojczystym języku, a nie jako nieprzejrzyste identyfikatory.

+0

Jest to w zasadzie to, czego szukam, ale w czymś, co działa z prostym C++ (bez frameworka). – Nick

+0

Tak, pomyślałem, że to może być coś, czego szukasz. Niestety, nie znam żadnych czystych wersji C++ takiego projektu, ale nie wydaje mi się (jak dla mnie), że trudno byłoby go napisać. Kluczowym uproszczeniem jest narzędzie "genstrings" do skanowania źródła i tworzenia plików. –

3

Używamy angielskiego ciągu jako identyfikatora.

Jeśli nie uda się wyszukać z międzynarodowego obiektu zasobów (załadowanego z zainstalowanego dll I18N), domyślnie ustawimy ciąg znaków ID.

Kod wygląda następująco:

doAction(I18N.get("Press OK to continue")); 

Jako część procesu kompilacji mamy skrypt Perla, który analizuje wszystkie źródła stałych łańcuchowych. Buduje plik tymczasowy wszystkich ciągów w aplikacji, a następnie porównuje je z ciągami zasobów w każdym lokalnym, aby sprawdzić, czy istnieją. Wszelkie brakujące ciągi generują wiadomość e-mail do odpowiedniego zespołu tłumaczy.

Możemy mieć wiele bibliotek dll dla każdego lokalnego. Nazwa DLL jest oparta na RFC 3066
język [_territory] [. Zestaw kodów] [@ modyfikator]

Staramy i rozpakować locale z urządzenia i być jak najbardziej konkretny podczas ładowania dll I18N ale awaryjna do mniej konkretnych odmian lokalnych, jeśli nie ma bardziej szczegółowej wersji.

Przykład:

W Wielkiej Brytanii: Jeśli lokalny był pl_PL.UTF-8
(używam terminu luźno dll nie w specyficznym sensie okien).

Najpierw poszukaj I18N.en_GB.UTF-8 dll. Jeśli ta biblioteka DLL nie istnieje, należy ją cofnąć do wersji I18N.en_PL. Jeśli dll nie istnieje spaść z powrotem do I18N.en Jeśli dll nie istnieje spadek Beck I18N.default

Jedynym wyjątkiem od tej reguły jest: chiński uproszczony (zh_CN), gdzie jest awaryjna US English (en_US). Jeśli urządzenie nie obsługuje uproszczonego języka chińskiego, jest mało prawdopodobne, że obsługuje pełne chińskie.

+0

Brzmi podobnie do tego, co robię, ale jak wyodrębnić wszystkie ciągi, aby utworzyć biblioteki zasobów? Czy jest to robione ręcznie? – Nick

+0

Ok, więc robisz coś bardzo podobnego do tego, co robię (poza tym, że wybrałem resx i korzystasz z natywnych bibliotek zasobów). Fajna okazja, sprawia, że ​​czuję się dużo lepiej w związku z podejściem, które podejmuję. Zostawię to otwarte na więcej komentarzy, ale wydaje się to dobrym podejściem. – Nick

+2

GNU gettext już to robi, więc po prostu wymyślono (niższą wersję) koło. –

0

Ponieważ jest otwarty na opinie, oto jak to robię.

Mój zlokalizowany plik tekstowy to prosty plik tekstowy rozdzielany tabulatorami, który można załadować do programu Excel i edytować. Pierwsza kolumna jest dla zdefiniowania i każda kolumna po prawej stronie jest kolejny język, na przykład:

ID    ENGLISH  FRENCH GERMAN 
STRING_YES  YES   OUI  YA 
STRING_NO  NO   NON  NEIN 

Wtedy w moim makefile jest krokiem cusom build który generuje plik strings.h i strings.dat . W moim przypadku tworzy on listę enum dla ids stringów, a następnie plik binarny z przesunięciami dla tekstu. Ponieważ w mojej aplikacji użytkownik może zmienić język w dowolnym momencie, mam je wszystkie w pamięci, ale w razie potrzeby można łatwo zmusić swojego preprocesora do wygenerowania innego pliku wyjściowego dla każdego języka.

Rzeczą, która mi się podoba w tym projekcie jest to, że jeśli brakuje jakichkolwiek ciągów znaków, otrzymam błąd kompilacji, natomiast jeśli łańcuchy znaków byłyby wyszukiwane w czasie wykonywania, to może nie wiedzieć o brakującym łańcuchu w rzadko używanej części kod do później.

0

Chcesz zaawansowanego narzędzia, które zawsze chciałem napisać, ale nigdy nie miałem czasu. Jeśli nie możesz znaleźć takiego narzędzia, możesz chcieć powrócić do moich klas CMSg() i CFMsg(), które pozwalają w łatwy sposób pobierać ciągi z tabeli zasobów.(CFMsg zapewnia nawet wrapper One-Liner FormatMessage I tak, w przypadku braku tego narzędzia, którego szukasz, zachowanie kopii komentarza w komentarzu jest dobrym rozwiązaniem. Jeśli chodzi o desynchronizację komentarza, pamiętaj o literałach ciągów są bardzo rzadko zmieniane.

http://www.codeproject.com/KB/string/stringtable.aspx

BTW, rodzime programy Win32 i .NET programy mają zupełnie inny zarządzanie zasobami pamięci masowej. będziesz miał trudności ze znalezieniem wspólnego rozwiązania dla obu stron.

1

Twój rozwiązanie jest dość podobne do rozwiązania Unix/Linux "gettext". W rzeczywistości nie trzeba pisać routingu ekstrakcji nes.

Nie jestem pewien, dlaczego chcesz makro _RESTRING obsługiwać wiele argumentów. Mój kod (przy użyciu obsługi wxWidgets dla gettext) wygląda następująco: MyString.Format(_("Some string with variable %ls"), _("variable"));. Oznacza to, że String :: Format (...) pobiera dwa indywidualnie tłumaczone argumenty. Z perspektywy czasu, boost :: Format byłby lepszy, ale też pozwoli boost::format(_("Some string with variable %1")) % _("variable");

(Używamy _() makro dla zwięzłość)

+0

Chciałem, aby makro obsługiwał wbudowane formatowanie varargs, głównie dla wygody. W przeciwnym razie musiałbym umieścić coś innego wokół, prawdopodobnie z inną deklaracją zmiennej łańcuchowej, która jest zmarnowanym kodem. – Nick

+0

Używamy gettext w projekcie wielobajtowym wraz z zasobami wielojęzycznymi. Problem polegał na tym, że nie możemy używać licencji GPL na gettext, musieliśmy napisać własną. Kolejną kwestią jest synchronizacja ciągów w zakresie parametrów formatowania: printf (_ ("Chcę tego:% .20s i to:% f% .3s")); Narzędzie zewnętrzne powinno synchronizować. – BlackBada

1

Prostym sposobem jest użycie tylko identyfikatory smyczkowych w kodzie - nie literalne smyczki. Następnie można utworzyć różne wersje pliku .rc dla każdego języka i albo utworzyć biblioteki DLL z zasobami, albo po prostu różne kompilacje językowe.

Istnieje kilka shareware sharestohelp, w których można zlokalizować plik rc, który obsługuje zmianę rozmiaru elementów dialogowych dla języków z dłuższymi słowami i ostrzega przed brakującymi tłumaczeniami.

Bardziej skomplikowanym problemem jest szyk wyrazów, jeśli masz kilka numerów w printf, które muszą być w innej kolejności dla gramatyki innego języka. Istnieje kilka rozszerzonych klas printf na codeproject, które pozwalają ci określić takie rzeczy, jak printf ("word% 1s and% 2s", var1, var2), dzięki czemu możesz w razie potrzeby zmienić% 1s i% 2s.