Introduce additional language setting "browser/Browser Language" for UI internationalization.

If language is set to "browser" the client/user browser language is used to choose from
available translation.
simply: one users browser speaks English -> YaCy responds in English, other users browser speaks French -> YaCy responds in French.

! To make a translation/language available you have to activate the language once ! 
(or manually use the utility class TranslateAll)
In ConfigBasic.html availabel translations are marked green on setting language=Browser
The client language is determined by http header Accept-Language (checked in DefaultServlet)
pull/68/head
reger 9 years ago
parent 3b47a07dd1
commit 16e8ed3f01

@ -33,14 +33,15 @@
<li>
<img src="env/grafics/ok.png" height="16" width="16" alt="ok" />&nbsp;Select a language for the interface:<br />
<fieldset>
<input type="radio" name="language" value="default" id="lang_en" onchange="this.form.submit()" #(lang_en)#::checked="checked"#(/lang_en)# /><label for="lang_en">English</label>
<input type="radio" name="language" value="de" id="lang_de" onchange="this.form.submit()" #(lang_de)#::checked="checked"#(/lang_de)# /><label for="lang_de">Deutsch</label>&nbsp;
<input type="radio" name="language" value="fr" id="lang_fr" onchange="this.form.submit()" #(lang_fr)#::checked="checked"#(/lang_fr)# /><label for="lang_fr">Fran&ccedil;ais</label>&nbsp;
<input type="radio" name="language" value="cn" id="lang_cn" onchange="this.form.submit()" #(lang_cn)#::checked="checked"#(/lang_cn)# /><label for="lang_cn">&#27721;&#35821;/&#28450;&#35486</label>
<input type="radio" name="language" value="ru" id="lang_ru" onchange="this.form.submit()" #(lang_ru)#::checked="checked"#(/lang_ru)# /><label for="lang_ru">&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;</label>
<input type="radio" name="language" value="uk" id="lang_uk" onchange="this.form.submit()" #(lang_uk)#::checked="checked"#(/lang_uk)# /><label for="lang_uk">&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;</label>
<input type="radio" name="language" value="hi" id="lang_hi" onchange="this.form.submit()" #(lang_hi)#::checked="checked"#(/lang_hi)# /><label for="lang_hi">&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;</label>
<input type="radio" name="language" value="ja" id="lang_ja" onchange="this.form.submit()" #(lang_ja)#::checked="checked"#(/lang_ja)# /><label for="lang_ja">&#26085;&#26412;&#35486;</label>
<input type="radio" name="language" value="browser" id="lang_browser" onchange="this.form.submit()" #(lang_browser)#::checked="checked"#(/lang_browser)# /><label for="lang_browser">Browser</label>&nbsp;
<input type="radio" name="language" value="default" id="lang_en" onchange="this.form.submit()" #(lang_en)#::checked="checked"#(/lang_en)# /><label for="lang_en" class="#[active_en]#">English</label>&nbsp;
<input type="radio" name="language" value="de" id="lang_de" onchange="this.form.submit()" #(lang_de)#::checked="checked"#(/lang_de)# /><label class="#[active_de]#" for="lang_de">Deutsch</label>&nbsp;
<input type="radio" name="language" value="fr" id="lang_fr" onchange="this.form.submit()" #(lang_fr)#::checked="checked"#(/lang_fr)# /><label class="#[active_fr]#" for="lang_fr">Fran&ccedil;ais</label>&nbsp;
<input type="radio" name="language" value="cn" id="lang_cn" onchange="this.form.submit()" #(lang_cn)#::checked="checked"#(/lang_cn)# /><label class="#[active_cn]#" for="lang_cn">&#27721;&#35821;/&#28450;&#35486</label>
<input type="radio" name="language" value="ru" id="lang_ru" onchange="this.form.submit()" #(lang_ru)#::checked="checked"#(/lang_ru)# /><label class="#[active_ru]#" for="lang_ru">&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;</label>
<input type="radio" name="language" value="uk" id="lang_uk" onchange="this.form.submit()" #(lang_uk)#::checked="checked"#(/lang_uk)# /><label class="#[active_uk]#" for="lang_uk">&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;</label>
<input type="radio" name="language" value="hi" id="lang_hi" onchange="this.form.submit()" #(lang_hi)#::checked="checked"#(/lang_hi)# /><label class="#[active_hi]#" for="lang_hi">&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;</label>
<input type="radio" name="language" value="ja" id="lang_ja" onchange="this.form.submit()" #(lang_ja)#::checked="checked"#(/lang_ja)# /><label class="#[active_ja]#" for="lang_ja">&#26085;&#26412;&#35486;</label>
</fieldset>
</li>
<!-- take care that no other items are changed, but also change the former if no js is enabled -->

@ -31,11 +31,13 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;
import net.yacy.cora.protocol.Domains;
import net.yacy.cora.protocol.HeaderFramework;
import net.yacy.cora.protocol.RequestHeader;
import net.yacy.data.Translator;
import net.yacy.data.WorkTables;
import net.yacy.http.YaCyHttpServer;
import net.yacy.kelondro.workflow.InstantBusyThread;
@ -63,7 +65,7 @@ public class ConfigBasic {
final Switchboard sb = (Switchboard) env;
final serverObjects prop = new serverObjects();
final File langPath = new File(sb.getAppPath("locale.source", "locales").getAbsolutePath());
String lang = env.getConfig("locale.language", "default");
String lang = env.getConfig("locale.language", "browser");
final int authentication = sb.adminAuthenticated(header);
if (authentication < 2) {
@ -269,6 +271,7 @@ public class ConfigBasic {
prop.put("defaultPort", env.getLocalPort());
prop.put("withsslenabled", env.getConfigBool("server.https", false) ? 1 : 0);
lang = env.getConfig("locale.language", "default"); // re-assign lang, may have changed
prop.put("lang_browser", "0"); // for client browser language dependent
prop.put("lang_de", "0");
prop.put("lang_fr", "0");
prop.put("lang_cn", "0");
@ -281,6 +284,28 @@ public class ConfigBasic {
} else {
prop.put("lang_" + lang, "1");
}
// set label class (green background) for active translation
if (lang.equals("browser")) {
List<String> l = Translator.activeTranslations();
prop.put("active_cn", l.contains("cn") ? "label-success" : "");
prop.put("active_de", l.contains("de") ? "label-success" : "");
prop.put("active_fr", l.contains("fr") ? "label-success" : "");
prop.put("active_hi", l.contains("hi") ? "label-success" : "");
prop.put("active_jp", l.contains("jp") ? "label-success" : "");
prop.put("active_ru", l.contains("ru") ? "label-success" : "");
prop.put("active_uk", l.contains("uk") ? "label-success" : "");
prop.put("active_en", "label-success");
} else {
prop.put("active_de", "");
prop.put("active_fr", "");
prop.put("active_hi", "");
prop.put("active_cn", "");
prop.put("active_ru", "");
prop.put("active_uk", "");
prop.put("active_en", "");
prop.put("active_jp", "");
}
return prop;
}
}

@ -81,7 +81,7 @@ public class ConfigLanguage_p {
* read from the language directory. This is very important to prevent
* directory traversal attacks!
*/
if (langFiles.contains(selectedLanguage) || selectedLanguage.startsWith("default")) {
if (langFiles.contains(selectedLanguage) || selectedLanguage.startsWith("default") || selectedLanguage.startsWith("browser")) {
new TranslatorXliff().changeLang(env, langPath, selectedLanguage);
}
@ -142,27 +142,28 @@ public class ConfigLanguage_p {
final Map<String, String> langNames = Translator.langMap(env);
//virtual entry
prop.put("langlist_0_file", "default");
prop.put("langlist_0_name", ((langNames.get("default") == null) ? "default" : langNames.get("default")));
prop.put("langlist_0_file", "browser");
prop.put("langlist_0_name", ((langNames.get("browser") == null) ? "browser" : langNames.get("browser")));
prop.put("langlist_0_selected", "selected=\"selected\"");
prop.put("langlist_1_file", "default");
prop.put("langlist_1_name", ((langNames.get("default") == null) ? "default" : langNames.get("default")));
int count = 0;
for (final String langFile : langFiles) {
//+1 because of the virtual entry "default" at top
int count = 2; //+2 because of the virtual entry "browser" and "default" at top
for (final String langFile : langFiles) {
final String langKey = langFile.substring(0, langFile.length() -4);
final String langName = langNames.get(langKey);
prop.put("langlist_" + (count + 1) + "_file", langFile);
prop.put("langlist_" + (count + 1) + "_name", ((langName == null) ? langKey : langName));
prop.put("langlist_" + (count) + "_file", langFile);
prop.put("langlist_" + (count) + "_name", ((langName == null) ? langKey : langName));
if(env.getConfig("locale.language", "default").equals(langKey)) {
prop.put("langlist_" + (count + 1) + "_selected", "selected=\"selected\"");
prop.put("langlist_" + (count) + "_selected", "selected=\"selected\"");
prop.put("langlist_0_selected", " "); // reset Default
} else {
prop.put("langlist_" + (count + 1) + "_selected", " ");
prop.put("langlist_" + (count) + "_selected", " ");
}
count++;
}
prop.put("langlist", (count + 1));
prop.put("langlist", (count));
//is done by Translationtemplate
//langName = (String) langNames.get(env.getConfig("locale.language", "default"));

@ -40,6 +40,7 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@ -53,6 +54,7 @@ import net.yacy.document.SentenceReader;
import net.yacy.kelondro.util.FileUtils;
import net.yacy.kelondro.util.Formatter;
import net.yacy.peers.Seed;
import net.yacy.search.Switchboard;
import net.yacy.search.SwitchboardConstants;
import net.yacy.server.serverSwitch;
import net.yacy.utils.translation.ExtensionsFileFilter;
@ -274,6 +276,7 @@ public class Translator {
public static Map<String, String> langMap(@SuppressWarnings("unused") final serverSwitch env) {
final String[] ms = CommonPattern.COMMA.split(
"browser/Browser Language," +
"default/English,de/Deutsch,fr/Fran&ccedil;ais,nl/Nederlands,it/Italiano,es/Espa&ntilde;ol,pt/Portug&ecirc;s,fi/Suomi,se/Svenska,dk/Dansk," +
"gr/E&lambda;&lambda;&eta;v&iota;&kappa;&alpha;,sk/Slovensky,cn/&#27721;&#35821;/&#28450;&#35486;," +
"ru/&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;,uk/&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;," +
@ -294,6 +297,9 @@ public class Translator {
if ("default".equals(lang) || "default.lng".equals(lang)) {
env.setConfig("locale.language", "default");
ret = true;
} else if ("browser".equals(lang) || "browser.lng".equals(lang)) {
env.setConfig("locale.language", "browser");
ret = true;
} else {
final String htRootPath = env.getConfig(SwitchboardConstants.HTROOT_PATH, SwitchboardConstants.HTROOT_PATH_DEFAULT);
final File sourceDir = new File(env.getAppPath(), htRootPath);
@ -319,4 +325,24 @@ public class Translator {
public static List<String> langFiles(File langPath) {
return FileUtils.getDirListing(langPath, Translator.LANG_FILENAME_FILTER);
}
/**
* Helper to collect a list of available translations
* This looks in the locale directory for the subdirectories created on translation
* @return list of language-codes of available/active translations
*/
public static List<String> activeTranslations() {
Switchboard sb = Switchboard.getSwitchboard();
File localePath;
if (sb != null)
localePath = sb.getDataPath("locale.translated_html", "DATA/LOCALE/htroot");
else
localePath = new File ("DATA/LOCALE/htroot");
List<String> dirlist = new ArrayList<String>(); // get list of language subdirectories
File[] list = localePath.listFiles();
for (File f : list) {
if (f.isDirectory()) dirlist.add(f.getName()); // filter directories to add to result
}
return dirlist;
}
}

@ -811,7 +811,15 @@ public class YaCyDefaultServlet extends HttpServlet {
protected void handleTemplate(String target, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
Switchboard sb = Switchboard.getSwitchboard();
String localeSelection = sb.getConfig("locale.language", "default");
String localeSelection = sb.getConfig("locale.language", "browser");
if (localeSelection.endsWith("browser")) {
String lng = request.getLocale().getLanguage();
if (lng.equalsIgnoreCase("en")) { // because en is handled as "default" in localizer
localeSelection = "default";
} else {
localeSelection = lng;
}
}
File targetFile = getLocalizedFile(target, localeSelection);
File targetClass = rewriteClassFile(_resourceBase.addPath(target).getFile());
String targetExt = target.substring(target.lastIndexOf('.') + 1);
@ -994,6 +1002,7 @@ public class YaCyDefaultServlet extends HttpServlet {
templatePatterns.put("navigation-advanced_authorized", authorized ? 1 : 0);
templatePatterns.put(SwitchboardConstants.GREETING_HOMEPAGE, sb.getConfig(SwitchboardConstants.GREETING_HOMEPAGE, ""));
templatePatterns.put(SwitchboardConstants.GREETING_SMALL_IMAGE, sb.getConfig(SwitchboardConstants.GREETING_SMALL_IMAGE, ""));
templatePatterns.put("clientlanguage", localeSelection);
String mimeType = Classification.ext2mime(targetExt, MimeTypes.Type.TEXT_HTML.asString());

@ -213,6 +213,7 @@ public final class TemplateEngine {
byte[] replacement;
int bb;
final ByteBuffer structure = new ByteBuffer();
final String clientbrowserlang = pattern.get("clientlanguage"); // preferred language or null (used for include files)
while (transferUntil(pis, out, hashChar)) {
bb = pis.read();
keyStream.reset();
@ -416,7 +417,7 @@ public final class TemplateEngine {
BufferedReader br = null;
try{
//br = new BufferedReader(new InputStreamReader(new FileInputStream( filename ))); //Simple Include
br = new BufferedReader( new InputStreamReader(new FileInputStream( HTTPDFileHandler.getLocalizedFile(UTF8.String(filename))), StandardCharsets.UTF_8) ); //YaCy (with Locales)
br = new BufferedReader( new InputStreamReader(new FileInputStream( HTTPDFileHandler.getLocalizedFile(UTF8.String(filename), clientbrowserlang)), StandardCharsets.UTF_8) ); //YaCy (with Locales)
//Read the Include
String line = "";
while ((line = br.readLine()) != null) {

@ -37,6 +37,8 @@ import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Semaphore;
@ -54,6 +56,7 @@ import net.yacy.cora.protocol.http.HTTPClient;
import net.yacy.cora.sorting.Array;
import net.yacy.cora.util.ConcurrentLog;
import net.yacy.crawler.retrieval.Response;
import net.yacy.data.Translator;
import net.yacy.document.Document;
import net.yacy.gui.YaCyApp;
import net.yacy.gui.framework.Browser;
@ -337,29 +340,43 @@ public final class yacy {
//regenerate Locales from Translationlist, if needed
final File locale_source = sb.getAppPath("locale.source", "locales");
final String lang = sb.getConfig("locale.language", "");
if (!lang.equals("") && !lang.equals("default")) { //locale is used
String currentRev = null;
try{
final BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(sb.getDataPath("locale.translated_html", "DATA/LOCALE/htroot"), lang+"/version" ))));
currentRev = br.readLine(); // may return null
br.close();
}catch(final IOException e){
//Error
}
// on lang=browser all active translation should be checked (because any could be requested by client)
List<String> langlist;
if (lang.endsWith("browser"))
langlist = Translator.activeTranslations(); // get all translated languages
else {
langlist = new ArrayList<String>();
langlist.add(lang);
}
for (String tmplang : langlist) {
if (!tmplang.equals("") && !tmplang.equals("default") && !tmplang.equals("browser")) { //locale is used
String currentRev = null;
try {
final BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(sb.getDataPath("locale.translated_html", "DATA/LOCALE/htroot"), tmplang + "/version"))));
currentRev = br.readLine(); // may return null
br.close();
} catch (final IOException e) {
//Error
}
if (currentRev == null || !currentRev.equals(sb.getConfig(Seed.VERSION, ""))) try { //is this another version?!
final File sourceDir = new File(sb.getConfig(SwitchboardConstants.HTROOT_PATH, SwitchboardConstants.HTROOT_PATH_DEFAULT));
final File destDir = new File(sb.getDataPath("locale.translated_html", "DATA/LOCALE/htroot"), lang);
if (new TranslatorXliff().translateFilesRecursive(sourceDir, destDir, new File(locale_source, lang + ".lng"), "html,template,inc", "locale")){ //translate it
//write the new Versionnumber
final BufferedWriter bw = new BufferedWriter(new PrintWriter(new FileWriter(new File(destDir, "version"))));
bw.write(sb.getConfig(Seed.VERSION, "Error getting Version"));
bw.close();
if (currentRev == null || !currentRev.equals(sb.getConfig(Seed.VERSION, ""))) {
try { //is this another version?!
final File sourceDir = new File(sb.getConfig(SwitchboardConstants.HTROOT_PATH, SwitchboardConstants.HTROOT_PATH_DEFAULT));
final File destDir = new File(sb.getDataPath("locale.translated_html", "DATA/LOCALE/htroot"), tmplang);
if (new TranslatorXliff().translateFilesRecursive(sourceDir, destDir, new File(locale_source, tmplang + ".lng"), "html,template,inc", "locale")) { //translate it
//write the new Versionnumber
final BufferedWriter bw = new BufferedWriter(new PrintWriter(new FileWriter(new File(destDir, "version"))));
bw.write(sb.getConfig(Seed.VERSION, "Error getting Version"));
bw.close();
}
} catch (final IOException e) {
}
}
} catch (final IOException e) {}
}
}
// initialize number formatter with this locale
Formatter.setLocale(lang);
if (!lang.equals("browser")) // "default" is handled by .setLocale()
Formatter.setLocale(lang);
// registering shutdown hook
ConcurrentLog.config("STARTUP", "Registering Shutdown Hook");

Loading…
Cancel
Save