Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into exportPdf
Browse files Browse the repository at this point in the history
* upstream/master:
  Change integrity message for names depending on database mode (#3330)
  Fix location bundle with fast access (#3327)
  Small code cleanup
  Fix "path not found" exception

# Conflicts:
#	src/main/java/org/jabref/logic/l10n/Localization.java
  • Loading branch information
Siedlerchr committed Oct 21, 2017
2 parents 713ecec + a7d40f1 commit cfa74e5
Show file tree
Hide file tree
Showing 28 changed files with 210 additions and 115 deletions.
4 changes: 3 additions & 1 deletion src/main/java/org/jabref/JabRefMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ private static void start(String[] args) {

Globals.prefs = preferences;
Globals.startBackgroundTasks();
Localization.setLanguage(preferences.get(JabRefPreferences.LANGUAGE));

// Note that the language was already set during the initialization of the preferences and it is safe to
// call the next function.
Globals.prefs.setLanguageDependentDefaultValues();

// Perform Migrations
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ public class JabRefFrame extends JFrame implements OutputPrinter {
Globals.getKeyPrefs().getKey(KeyBinding.OPEN_CONSOLE),
IconTheme.JabRefIcon.CONSOLE.getIcon());
private final AbstractAction pullChangesFromSharedDatabase = new GeneralAction(Actions.PULL_CHANGES_FROM_SHARED_DATABASE,
Localization.menuTitle("Pull_changes_from_shared_database"),
Localization.lang("Pull_changes_from_shared_database"),
Localization.menuTitle("Pull changes from shared database"),
Localization.lang("Pull changes from shared database"),
Globals.getKeyPrefs().getKey(KeyBinding.PULL_CHANGES_FROM_SHARED_DATABASE),
IconTheme.JabRefIcon.PULL.getIcon());
private final AbstractAction mark = new GeneralAction(Actions.MARK_ENTRIES, Localization.menuTitle("Mark entries"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import java.util.List;
import java.util.stream.Collectors;

import javafx.application.Platform;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;

import org.jabref.JabRefGUI;
import org.jabref.gui.maintable.MainTable;
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.OS;
import org.jabref.model.entry.BibEntry;
Expand Down Expand Up @@ -48,14 +48,11 @@ public void action() throws Exception {
}

// This works on Mac and Windows 10, but not on Ubuntu 16.04
Platform.runLater(new Runnable() {
@Override
public void run() {
final Clipboard clipboard = Clipboard.getSystemClipboard();
final ClipboardContent content = new ClipboardContent();
content.putHtml(sb.toString());
clipboard.setContent(content);
}
DefaultTaskExecutor.runInJavaFXThread(() -> {
final Clipboard clipboard = Clipboard.getSystemClipboard();
final ClipboardContent content = new ClipboardContent();
content.putHtml(sb.toString());
clipboard.setContent(content);
});

int copied = entriesWithKey.size();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private static Multimap<String, ValueChecker> getAllMap(BibDatabaseContext datab
fieldCheckers.put(field, new AbbreviationChecker());
}
for (String field : InternalBibtexFields.getPersonNameFields()) {
fieldCheckers.put(field, new PersonNamesChecker());
fieldCheckers.put(field, new PersonNamesChecker(databaseContext));
}
fieldCheckers.put(FieldName.BOOKTITLE, new BooktitleChecker());
fieldCheckers.put(FieldName.TITLE, new BracketChecker());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@
import java.util.Optional;

import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.AuthorList;
import org.jabref.model.strings.StringUtil;

public class PersonNamesChecker implements ValueChecker {

private final BibDatabaseMode bibMode;

public PersonNamesChecker(BibDatabaseContext databaseContext) {
this.bibMode = databaseContext.getMode();
}

@Override
public Optional<String> checkValue(String value) {
if (StringUtil.isBlank(value)) {
Expand All @@ -28,7 +36,7 @@ public Optional<String> checkValue(String value) {
AuthorList authorList = AuthorList.parse(value);
if (!authorList.getAsLastFirstNamesWithAnd(false).equals(value)
&& !authorList.getAsFirstLastNamesWithAnd().equals(value)) {
return Optional.of(Localization.lang("Names are not in the standard BibTeX format."));
return Optional.of(Localization.lang("Names are not in the standard %0 format.", bibMode.getFormattedName()));
}

return Optional.empty();
Expand Down
193 changes: 152 additions & 41 deletions src/main/java/org/jabref/logic/l10n/Localization.java
Original file line number Diff line number Diff line change
@@ -1,42 +1,103 @@
package org.jabref.logic.l10n;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* Provides handling for messages and menu entries in the preferred language of the user.
* <p>
* Notes: All messages and menu-entries in JabRef are stored in escaped form like "This_is_a_message". This message
* serves as key inside the {@link l10n} properties files that hold the translation for many languages. When a message
* is accessed, it needs to be unescaped and possible parameters that can appear in a message need to be filled with
* values.
* <p>
* This implementation loads the appropriate language by importing all keys/values from the correct bundle and stores
* them in unescaped form inside a {@link LocalizationBundle} which provides fast access because it caches the key-value
* pairs.
* <p>
* The access to this is given by the functions {@link Localization#lang(String, String...)} and {@link
* Localization#menuTitle(String, String...)} that developers should use whenever they use strings for the e.g. GUI that
* need to be translatable.
*/
public class Localization {

public static final String RESOURCE_PREFIX = "l10n/JabRef";
public static final String MENU_RESOURCE_PREFIX = "l10n/Menu";
public static final String BIBTEX = "BibTeX";
static final String RESOURCE_PREFIX = "l10n/JabRef";
static final String MENU_RESOURCE_PREFIX = "l10n/Menu";

private static final Log LOGGER = LogFactory.getLog(Localization.class);

private static ResourceBundle messages;
private static ResourceBundle menuTitles;
private static Locale locale;
private static LocalizationBundle localizedMessages;
private static LocalizationBundle localizedMenuTitles;

private Localization() {
}

public static LocalizationBundle getMessages() {
return new LocalizationBundle(messages);
/**
* Public access to all messages that are not menu-entries
*
* @param key The key of the message in unescaped form like "All fields"
* @param params Replacement strings for parameters %0, %1, etc.
* @return The message with replaced parameters
*/
public static String lang(String key, String... params) {
if (localizedMessages == null) {
// I'm logging this because it should never happen
LOGGER.error("Messages are not initialized.");
setLanguage("en");
}
return lookup(localizedMessages, "message", key, params);
}

/**
* Public access to menu entry messages
*
* @param key The key of the message in unescaped form like "Save all"
* @param params Replacement strings for parameters %0, %1, etc.
* @return The message with replaced parameters
*/
public static String menuTitle(String key, String... params) {
if (localizedMenuTitles == null) {
// I'm logging this because it should never happen
LOGGER.error("Menu entries are not initialized");
setLanguage("en");
}
return lookup(localizedMenuTitles, "menu item", key, params);
}

/**
* Sets the language and loads the appropriate translations. Note, that this function should be called before any
* other function of this class.
*
* @param language Language identifier like "en", "de", etc.
*/
public static void setLanguage(String language) {
Optional<Locale> knownLanguage = Languages.convertToSupportedLocale(language);
final Locale defaultLocale = Locale.getDefault();
if (!knownLanguage.isPresent()) {
LOGGER.warn("Language " + language + " is not supported by JabRef (Default:" + Locale.getDefault() + ")");
LOGGER.warn("Language " + language + " is not supported by JabRef (Default:" + defaultLocale + ")");
setLanguage("en");
return;
}

Locale locale = knownLanguage.get();
// avoid reinitialization of the language bundles
final Locale langLocale = knownLanguage.get();
if ((locale != null) && locale.equals(langLocale) && locale.equals(defaultLocale)) {
return;
}
locale = langLocale;
Locale.setDefault(locale);
javax.swing.JComponent.setDefaultLocale(locale);

Expand All @@ -49,53 +110,103 @@ public static void setLanguage(String language) {
}
}

/**
* Public access to the messages bundle for classes like AbstractView.
*
* @return The internally cashed bundle.
*/
public static LocalizationBundle getMessages() {
// avoid situations where this function is called before any language was set
if (locale == null) {
setLanguage("en");
}
return localizedMessages;
}

/**
* Creates and caches the language bundles used in JabRef for a particular language. This function first loads
* correct version of the "escaped" bundles that are given in {@link l10n}. After that, it stores the unescaped
* version in a cached {@link LocalizationBundle} for fast access.
*
* @param locale Localization to use.
*/
private static void createResourceBundles(Locale locale) {
messages = ResourceBundle.getBundle(RESOURCE_PREFIX, locale, new EncodingControl(StandardCharsets.UTF_8));
menuTitles = ResourceBundle.getBundle(MENU_RESOURCE_PREFIX, locale, new EncodingControl(StandardCharsets.UTF_8));
ResourceBundle messages = ResourceBundle.getBundle(RESOURCE_PREFIX, locale, new EncodingControl(StandardCharsets.UTF_8));
ResourceBundle menuTitles = ResourceBundle.getBundle(MENU_RESOURCE_PREFIX, locale, new EncodingControl(StandardCharsets.UTF_8));
Objects.requireNonNull(messages, "Could not load " + RESOURCE_PREFIX + " resource.");
Objects.requireNonNull(menuTitles, "Could not load " + MENU_RESOURCE_PREFIX + " resource.");
localizedMessages = new LocalizationBundle(createLookupMap(messages));
localizedMenuTitles = new LocalizationBundle(createLookupMap(menuTitles));
}

/**
* In the translation, %0, ..., %9 is replaced by the respective params given
* Helper function to create a HashMap from the key/value pairs of a bundle.
*
* @param resBundle the ResourceBundle to use
* @param idForErrorMessage output when translation is not found
* @param key the key to lookup in resBundle
* @param params a list of Strings to replace %0, %1, ...
* @return
* @param baseBundle JabRef language bundle with keys and values for translations.
* @return Lookup map for the baseBundle.
*/
protected static String translate(ResourceBundle resBundle, String idForErrorMessage, String key, String... params) {
Objects.requireNonNull(resBundle);
private static HashMap<String, String> createLookupMap(ResourceBundle baseBundle) {
final ArrayList<String> baseKeys = Collections.list(baseBundle.getKeys());
return new HashMap<>(baseKeys.stream().collect(
Collectors.toMap(
key -> new LocalizationKey(key).getTranslationValue(),
key -> new LocalizationKey(baseBundle.getString(key)).getTranslationValue())
));
}

String translation = null;
try {
String propertiesKey = new LocalizationKey(key).getPropertiesKeyUnescaped();
translation = resBundle.getString(propertiesKey);
} catch (MissingResourceException ex) {
/**
* This looks up a key in the bundle and replaces parameters %0, ..., %9 with the respective params given. Note that
* the keys are the "unescaped" strings from the bundle property files.
*
* @param bundle The {@link LocalizationBundle} which means either {@link Localization#localizedMenuTitles}
* or {@link Localization#localizedMessages}.
* @param idForErrorMessage Identifier-string when the translation is not found.
* @param key The lookup key.
* @param params The parameters that should be inserted into the message
* @return The final message with replaced parameters.
*/
private static String lookup(LocalizationBundle bundle, String idForErrorMessage, String key, String... params) {
Objects.requireNonNull(key);

String translation = bundle.containsKey(key) ? bundle.getString(key) : "";
if (translation.isEmpty()) {
LOGGER.warn("Warning: could not get " + idForErrorMessage + " translation for \"" + key + "\" for locale "
+ Locale.getDefault());
}
if ((translation == null) || translation.isEmpty()) {
LOGGER.warn("Warning: no " + idForErrorMessage + " translation for \"" + key + "\" for locale "
+ Locale.getDefault());

translation = key;
}

return new LocalizationKeyParams(translation, params).replacePlaceholders();
}

public static String lang(String key, String... params) {
if (messages == null) {
setLanguage("en");
/**
* A bundle for caching localized strings. Needed to support JavaFX inline binding.
*/
private static class LocalizationBundle extends ResourceBundle {

private final HashMap<String, String> lookup;

LocalizationBundle(HashMap<String, String> lookupMap) {
lookup = lookupMap;
}
return translate(messages, "message", key, params);
}

public static String menuTitle(String key, String... params) {
if (menuTitles == null) {
setLanguage("en");
public final Object handleGetObject(String key) {
Objects.requireNonNull(key);
return lookup.get(key);
}
return translate(menuTitles, "menu item", key, params);
}

@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(lookup.keySet());
}

@Override
protected Set<String> handleKeySet() {
return lookup.keySet();
}

@Override
public boolean containsKey(String key) {
return (key != null) && lookup.containsKey(key);
}
}
}

36 changes: 0 additions & 36 deletions src/main/java/org/jabref/logic/l10n/LocalizationBundle.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,12 @@ public Set<Path> findFilesByExtension(List<Path> directories, List<String> exten

Set<Path> result = new HashSet<>();
for (Path directory : directories) {
try (Stream<Path> files = Files.find(directory, Integer.MAX_VALUE, isFileWithCorrectExtension)) {
result.addAll(files.collect(Collectors.toSet()));
} catch (IOException e) {
LOGGER.error("Problem in finding files", e);
if (Files.exists(directory)) {
try (Stream<Path> files = Files.find(directory, Integer.MAX_VALUE, isFileWithCorrectExtension)) {
result.addAll(files.collect(Collectors.toSet()));
} catch (IOException e) {
LOGGER.error("Problem in finding files", e);
}
}
}
return result;
Expand Down
Loading

0 comments on commit cfa74e5

Please sign in to comment.