X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fgettext.c;h=4df641a28e7ce01f4eb1a8fda82878ae59140fa1;hb=faa7bc4175fa88b5fd6548b2e92cdc5497dfe692;hp=e0978b2c059b040645ccb84b328237463c23cf60;hpb=4760d0fc8089130b066045fa60d312030b0a34e3;p=citadel.git diff --git a/webcit/gettext.c b/webcit/gettext.c index e0978b2c0..4df641a28 100644 --- a/webcit/gettext.c +++ b/webcit/gettext.c @@ -1,144 +1,470 @@ -#define _GNU_SOURCE +/* + * Copyright (c) 1996-2012 by the citadel.org team + * + * This program is open source software. You can redistribute it and/or + * modify it under the terms of the GNU General Public License version 3. + * + * 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. + */ + #include "webcit.h" #include "webserver.h" +#define SEARCH_LANG 20 /* how many langs should we parse? */ #ifdef ENABLE_NLS - -static const char *AvailLang[] = { - "de_DE" +/* actual supported locales */ +const char *AvailLang[] = { + "C", + "bg_BG", + "cs_CZ", + "en_US", + "da_DK", + "de_DE", + "el_GR", + "en_GB", + "es_ES", + "et_EE", + "fi_FI", + "fr_FR", + "hu_HU", + "it_IT", + "nl_NL", + "pt_BR", + "ru_RU", + "zh_CN", + "he_IL", + "kk_KK", + "ro_RO", + "sl_SL", + "tr_TR", + "" }; +const char **AvailLangLoaded; +long nLocalesLoaded = 0; + +#ifdef HAVE_USELOCALE +locale_t *wc_locales; /* here we keep the parsed stuff */ +#endif + +/* Keep information about one locale */ typedef struct _lang_pref{ - char lang[16]; - char region[16]; - char *match; - double Priority; + char lang[16]; /* the language locale string */ + char region[16]; /* the region locale string */ + long priority; /* which priority does it have */ + int availability; /* do we know it? */ + int selectedlang; /* is this the selected language? */ } LangStruct; -/* TODO: we skip the language weightening so far. */ -/* Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' */ -/* Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 */ -void httplang_to_locale(const char *LocaleString) +/* parse browser locale header + * + * seems as most browsers just do a one after comma value even if more than 10 locales are available. Sample strings: + * opera: + * Accept-Language: sq;q=1.0,de;q=0.9,as;q=0.8,ar;q=0.7,bn;q=0.6,zh-cn;q=0.5,kn;q=0.4,ch;q=0.3,fo;q=0.2,gn;q=0.1,ce;q=0.1,ie;q=0.1 + * Firefox + * Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' + * Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 + * Accept-Language: de,en-us;q=0.9,it;q=0.9,de-de;q=0.8,en-ph;q=0.7,de-at;q=0.7,zh-cn;q=0.6,cy;q=0.5,ar-om;q=0.5,en-tt;q=0.4,xh;q=0.3,nl-be;q=0.3,cs;q=0.2,sv;q=0.1,tk;q=0.1 + */ + +void httplang_to_locale(StrBuf *LocaleString, wcsession *sess) { - char *locale = "C"; - LangStruct wanted_locales[20]; + LangStruct wanted_locales[SEARCH_LANG]; + LangStruct *ls; + int i = 0; int j = 0; - size_t len = strlen(LocaleString); - int nFound = 0; + /* size_t len = strlen(LocaleString); */ + long prio; + int av; + int nBest; int nParts; - const int nAvail = 1; /* Number of members in AvailLang */ - char *search = (char *) malloc(len); - locale_t my_Locale; - locale_t my_Empty_Locale; - - memcpy(search, LocaleString, len); - search[len] = '\0'; - nParts=num_tokens(search,','); - for (i=0; ((ipriority = StrTol(SBuf); } else { - wanted_locales[i].Priority=1.0; + ls->priority = 1000; } + /* get the locale part */ - extract_token(&sbuf[0],&buf[0],0,';',16); + StrBufExtract_token(SBuf, Buf, 0, ';'); + /* get the lang part, which should be allways there */ - extract_token(&wanted_locales[i].lang[0],&sbuf[0],0,'-',16); + extract_token(&ls->lang[0], + ChrPtr(SBuf), + 0, '-', + sizeof(ls->lang)); + /* get the area code if any. */ - if (num_tokens(&sbuf[0],'-')>1) { - extract_token(&wanted_locales[i].region[0],&sbuf[0],1,'-',16); + if (StrBufNum_tokens(SBuf, '-') > 1) { + extract_token(&ls->region[0], + ChrPtr(SBuf), + 1, '-', + sizeof(ls->region) + ); } else { /* no ara code? use lang code */ - blen=strlen(&wanted_locales[i].lang[0]); - memcpy(&wanted_locales[i].region[0], wanted_locales[i].lang,blen); - wanted_locales[i].region[blen]='\0'; - } /* area codes are uppercase */ - blen=strlen(&wanted_locales[i].region[0]); - for (j=0; jlang[0]); + memcpy(&ls->region[0], ls->lang, blen); + ls->region[blen] = '\0'; + } - /* todo: weight */ - if (nFound > 0) { - for (i = 0; i <= nFound; i++) { - for (j = 0; j < nAvail; j++) { - int ret = strncasecmp(&wanted_locales[i].lang[0], - AvailLang[j], - strlen(&wanted_locales[i].lang[0])); - if (!ret) { - locale = (char *) AvailLang[j]; //wanted_locales[i]; - i = nFound + 1; - j = nAvail + 1; - continue; - } - + /* area codes are uppercase */ + blen = strlen(&ls->region[0]); + for (j = 0; j < blen; j++) + { + int chars; + chars = toupper(ls->region[j]); + ls->region[j] = (char)chars; /* todo ? */ + } + snprintf(&lbuf[0], + sizeof(lbuf), + "%s_%s", + &ls->lang[0], + &ls->region[0]); + + /* check if we have this lang */ + ls->availability = 1; + ls->selectedlang = -1; + for (j = 0; j < nLocalesLoaded; j++) { + int result; + /* match against the LANG part */ + result = strcasecmp(&ls->lang[0], AvailLangLoaded[j]); + if ((result < 0) && (result < ls->availability)){ + ls->availability = result; + ls->selectedlang = j; + } + /* match against lang and locale */ + if (0 == strcasecmp(&lbuf[0], AvailLangLoaded[j])){ + ls->availability = 0; + ls->selectedlang = j; + j = nLocalesLoaded; } } + } + + prio = 0; + av = -1000; + nBest = -1; + for (i = 0; ((i < nParts) && (iavailability <= 0) + && (av < ls->availability) + && (prio < ls->priority) + && (ls->selectedlang != -1) + ) { + nBest = ls->selectedlang; + av = ls->availability; + prio = ls->priority; + } + } + if (nBest == -1) { + /* fall back to C */ + nBest=0; } + sess->selected_language = nBest; + syslog(9, "language found: %s", AvailLangLoaded[WC->selected_language]); + FreeStrBuf(&Buf); + FreeStrBuf(&SBuf); +} - len = strlen(locale); - memcpy(search, locale, len); - memcpy(&search[len], ".UTF8", 5); - search[len + 5] = '\0'; - my_Empty_Locale = newlocale(LC_ALL_MASK, NULL, NULL); /* create default locale */ - my_Locale = newlocale(LC_MESSAGES_MASK /*|LC_TIME_MASK FIXME */ , - search, my_Empty_Locale); - uselocale(my_Locale); - // freelocale(my_Locale); - // freelocale(my_Empty_Locale); - free(search); +/* + * show the language chooser on the login dialog + * depending on the browser locale change the sequence of the + * language chooser. + */ +void tmplput_offer_languages(StrBuf *Target, WCTemplputParams *TP) +{ +#ifdef HAVE_USELOCALE + int i; + + wc_printf("\n"); +#else + wc_printf("%s", (getenv("LANG") ? getenv("LANG") : "C")); +#endif } +/* + * Set the selected language for this session. + */ +void set_selected_language(const char *lang) { +#ifdef HAVE_USELOCALE + int i; + for (i = 0; iselected_language = i; + break; + } + } #endif +} +/* + * Activate the selected language for this session. + */ +void go_selected_language(void) { +#ifdef HAVE_USELOCALE + wcsession *WCC = WC; + if (WCC->selected_language < 0) return; + uselocale(wc_locales[WCC->selected_language]); /* switch locales */ + textdomain(textdomain(NULL)); /* clear the cache */ +#else + char *language; + + language = getenv("LANG"); + setlocale(LC_MESSAGES, language); +#endif +} /* - // the web browser sends '-', we need '_' + * Deactivate the selected language for this session. + */ +void stop_selected_language(void) { +#ifdef HAVE_USELOCALE + uselocale(LC_GLOBAL_LOCALE); /* switch locales */ + textdomain(textdomain(NULL)); /* clear the cache */ +#endif +} - for (i = 0; i < len; i++) - if (search[i] == '-') - search[i] = '_'; - nFound=i; - nParts=num_tokens(search,','); - if (nParts>0) - { - extract_token(prefers,search, 1,';',len); - extract_token(langs,search, 0,';',len); +/* + * Create a locale_t for each available language + */ +void initialize_locales(void) { + int nLocales; + int i; + char *language = NULL; + +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + syslog(9, "Text domain: %s", textdomain("webcit")); + syslog(9, "Message catalog directory: %s", bindtextdomain(textdomain(NULL), LOCALEDIR"/locale")); + syslog(9, "Text domain Charset: %s", bind_textdomain_codeset("webcit","UTF8")); +#endif + + nLocales = 0; + while (!IsEmptyStr(AvailLang[nLocales])) + nLocales++; + + language = getenv("WEBCIT_LANG"); + if ((language) && (!IsEmptyStr(language)) && (strcmp(language, "UNLIMITED") != 0)) { + syslog(9, "Nailing locale to %s", language); + } + else language = NULL; + + AvailLangLoaded = malloc (sizeof(char*) * nLocales); + memset(AvailLangLoaded, 0, sizeof(char*) * nLocales); +#ifdef HAVE_USELOCALE + wc_locales = malloc (sizeof(locale_t) * nLocales); + memset(wc_locales,0, sizeof(locale_t) * nLocales); + wc_locales[0] = newlocale(LC_ALL_MASK, NULL, NULL); +#endif + + for (i = 1; i < nLocales; ++i) { +#ifdef HAVE_USELOCALE + wc_locales[nLocalesLoaded] = newlocale( + (LC_MESSAGES_MASK|LC_TIME_MASK), + AvailLang[i], + wc_locales[0] + ); + if (wc_locales[nLocalesLoaded] == NULL) { + syslog(1, "locale for %s disabled: %s (domain: %s, path: %s)", + AvailLang[i], + strerror(errno), + textdomain(NULL), + bindtextdomain(textdomain(NULL), NULL) + ); } - else - { - free(prefers); - prefers=NULL; - memcpy(search, len); - search[len] = '\0'; + else { + syslog(3, "Found locale: %s", AvailLang[i]); + AvailLangLoaded[nLocalesLoaded] = AvailLang[i]; + nLocalesLoaded++; } - i = 0; - while ( !done && (nFound < 10)) { - if ((search[i] == ',') || (search[i] == ';') || (search[i] == '\0')) - { - if ((search[i] == ';') || (search[i] == '\0')) - done = 1; - search[i] = '\0'; - wanted_locales[nFound] = (char *) &search[j]; - j = i + 1; - nFound++; +#else + if ((language != NULL) && (strcmp(language, AvailLang[i]) == 0)) { + setenv("LANG", AvailLang[i], 1); + AvailLangLoaded[nLocalesLoaded] = AvailLang[i]; + setlocale(LC_MESSAGES, AvailLang[i]); + nLocalesLoaded++; + } + else if (nLocalesLoaded == 0) { + setenv("LANG", AvailLang[i], 1); + AvailLangLoaded[nLocalesLoaded] = AvailLang[i]; + nLocalesLoaded++; } +#endif + } + if ((language != NULL) && (nLocalesLoaded == 0)) { + syslog(1, "Your selected locale [%s] isn't available on your system. falling back to C", language); +#ifndef HAVE_USELOCALE + setlocale(LC_MESSAGES, AvailLang[0]); + setenv("LANG", AvailLang[0], 1); +#endif + AvailLangLoaded[0] = AvailLang[0]; + nLocalesLoaded = 1; + } +} + + +void +ServerShutdownModule_GETTEXT +(void) +{ +#ifdef HAVE_USELOCALE + int i; + for (i = 0; i < nLocalesLoaded; ++i) { + freelocale(wc_locales[i]); + } + free(wc_locales); +#endif + free(AvailLangLoaded); +} + +#else /* ENABLE_NLS */ +const char *AvailLang[] = { + "C", + "" +}; + +/* dummy for non NLS enabled systems */ +void tmplput_offer_languages(StrBuf *Target, WCTemplputParams *TP) +{ + wc_printf("English (US)"); +} + +/* dummy for non NLS enabled systems */ +void set_selected_language(const char *lang) { +} + +/* dummy for non NLS enabled systems */ +void go_selected_language(void) { +} + +/* dummy for non NLS enabled systems */ +void stop_selected_language(void) { +} + +void initialize_locales(void) { +} + +#endif /* ENABLE_NLS */ + + +void TmplGettext(StrBuf *Target, WCTemplputParams *TP) +{ + StrBufAppendBufPlain(Target, _(TP->Tokens->Params[0]->Start), -1, 0); +} + - i++; +/* + * Returns the language currently in use. + * This function returns a static string, so don't do anything stupid please. + */ +const char *get_selected_language(void) { +#ifdef ENABLE_NLS +#ifdef HAVE_USELOCALE + return AvailLang[WC->selected_language]; +#else + return "en"; +#endif +#else + return "en"; +#endif +} + + +void Header_HandleAcceptLanguage(StrBuf *Line, ParsedHttpHdrs *hdr) +{ + hdr->HR.browser_language = Line; +} + + +void +InitModule_GETTEXT +(void) +{ + initialize_locales(); + + RegisterHeaderHandler(HKEY("ACCEPT-LANGUAGE"), + Header_HandleAcceptLanguage); + + RegisterNamespace("LANG:SELECT", 0, 0, + tmplput_offer_languages, NULL, CTX_NONE); +} + + +void +SessionNewModule_GETTEXT +(wcsession *sess) +{ +#ifdef ENABLE_NLS + if ( (sess != NULL) + && (!sess->Hdr->HR.Static) + && (sess->Hdr->HR.browser_language != NULL) + ) { + httplang_to_locale(sess->Hdr->HR.browser_language, sess); } -*/ +#endif +} + +void +SessionAttachModule_GETTEXT +(wcsession *sess) +{ +#ifdef ENABLE_NLS + go_selected_language(); /* set locale */ +#endif +} + +void +SessionDestroyModule_GETTEXT +(wcsession *sess) +{ +#ifdef ENABLE_NLS + stop_selected_language(); /* unset locale */ +#endif +}