Skip to content

De-localize preferences#8050

Closed
btut wants to merge 28 commits into
mainfrom
unlocalized-preferences
Closed

De-localize preferences#8050
btut wants to merge 28 commits into
mainfrom
unlocalized-preferences

Conversation

@btut

@btut btut commented Sep 1, 2021

Copy link
Copy Markdown
Contributor

This PR implements ADR-0023

  • Change in CHANGELOG.md described in a way that is understandable for the average user (if applicable)
  • Tests created for changes (if applicable)
  • Manually tested changed features in running JabRef (always required)
  • Screenshots added in PR description (for UI changes)
  • Checked documentation: Is the information available and up to date? If not, I created an issue at https://github.com/JabRef/user-documentation/issues or, even better, I submitted a pull request to the documentation repository.

for (Map.Entry<String, Set<Field>> tab : entryEditorPreferences.getEntryEditorTabList().entrySet()) {
entryEditorTabs.add(new UserDefinedFieldsTab(tab.getKey(), tab.getValue(), databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, Globals.entryTypesManager, ExternalFileTypes.getInstance(), Globals.TASK_EXECUTOR, Globals.journalAbbreviationRepository));
String key = tab.getKey();
if (key.equals(preferencesService.getDefaults().get(JabRefPreferences.CUSTOM_TAB_NAME + "_def" + i))) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan that the constants like CUSTOM_TAB_NAME are exposed. They are an implementation detail and shouldn't exposed outside. So for example, here you could add the default tab names to the entryEditorPreferences.

But actually I'm not sure why you need to check if its a default tab name in the first place. I think it's a nice touch that if the user chooses a name that we happen to have localized, why not always show the localized string. Simple example: the user decides to switch the abstract and the comment tab.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi!

I'm not a big fan that the constants like CUSTOM_TAB_NAME are exposed.

I agree it would be better to have that directly in the Preferences Objects. This goes for the keys and the default values. So each Preference Object would have it's own keys and it's own defaults-Map for those Strings. @calixtus argues this makes the list of preferences harder to implement.

But actually I'm not sure why you need to check if its a default tab name in the first place.

Some background on this. We had some preferences that need localization. So for example the Entry-Editor-Tab name 'Abstract'. The default for the preference is 'Abstract'. JabRef should show 'Abstract' for english and 'Zusammenfassung' for german. Now we can think of a couple of cases:

When loading:

  • The preference that was loaded says 'Abstract'. We need to localize that since the language may not be set to english
  • The preference says something else. We don't localize this because it was changed by the user.

When storing:

  • JabRef is set to english. The new value to store says 'Abstract'. We can store that.
  • JabRef is set to german. The new value says 'Zusammenfassung'. This was localized. We actually want to store 'Abstract' so it can be correctly localized next time it is needed (also if the language was changed in the meantime).

why not always show the localized string

When the user changes the name, we cannot localize it anymore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also see ADR0023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So each Preference Object would have it's own keys and it's own defaults-Map for those Strings.

Not necessarily. My proposal would be to have a variable defaultTabNames in the preference object, which is initialized to the value of getDefaults().get(JabRefPreferences.CUSTOM_TAB_NAME + "_def" + i)) using a constructor argument when the preference object is initialized in JabRefPreferences. In this way, you can access the default outside of JabRefPreferences without needing to worry how the preferences are stored there.

When the user changes the name, we cannot localize it anymore.

We can still try to localize it, or not? So if the user puts "Abstract" as a custom tab name (assume it's not in default for the moment), then when displaying the tab, we get the localized string, say "Zusamenfassung" if the language is set to german. If the user already puts "Zusammenfassung" in the preferences, then Localization.lang doesn't do anything and returns "Zusammenfassung" again. There might be very rare cases where the user puts in a word in some language, which is a valid english word with a different translation (artificial example: Chinese user puts yue (moon) in, which is a valid english word (synonymy for Cantonese), so that Jabref would show Guǎngdōng huà (Cantonese in Chinese)). But these cases should be super rare with our limited dictionary. Would make the implementation a lot easier and might actually be what the user hopes to get

}
Set<Field> fields = tab.getValue();
if (fields.equals(FieldFactory.parseFieldList((String) Objects.requireNonNullElse(preferencesService.getDefaults().get(JabRefPreferences.CUSTOM_TAB_FIELDS + "_def" + i), "")))) {
fields = fields.stream().map(Field::getName).map(Localization::lang).map(FieldFactory::parseField).collect(Collectors.toSet());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if that really works as intended, or if it now also stores the value typed in the field under the localized field name. But maybe we shouldn't localize field names at all or be consistently and localize all field names, also in other tabs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to store pref X only as X if it was changed by the user. Otherwise store the default value.
This is needed because JabRef writes all preferences once it's closed. Otherwise, if we localize 'Abstract' to 'Zusammenfassung' it would be stored as 'Zusammenfassung' and cannot be localized again. So no matter what language I set, JabRef would always say 'Zusammenfassung'.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But isn't it then also storing the value (what the user types into the field in the entry editor) in the bibtex field "Zusammenfassung" because the variable field only contains localized fields.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No the value should not be recorded in the preferences, only the heading of the tab and the fields to display in that tab. Those strings can be changed in the preferences, not directly in the entry editor AFAIK.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant not the preferences, but how the field is written in the Bib file

Comment thread src/main/java/org/jabref/preferences/JabRefPreferences.java Outdated
@koppor

koppor commented Sep 2, 2021

Copy link
Copy Markdown
Member

For checking the localization strings, I switched to Java Parser. Needed to fix two parsing errors (javaparser/javaparser#3362 and javaparser/javaparser#3361). I build a custom JAR containing these patches. Works great here.

Our decision was to allow variable references in Localization.lang if the ADR 23 is referenced.

Comment thread docs/adr/0023-localized-preferences.md Outdated
Store the preference only when it was changed by the user. Otherwise, leave it empty.

* Good, because When a consumer gets such an empty preference, it knows that it needs to read the default and localize it.
* Bad, because this won't work if users actually want something to be empty.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the large amount of hacks / workarounds that are needed to make our localization tests work with the third approach, maybe this second approach is more efficient? One could use null / empty optional as the default value so that users can still provide an empty string if they wish.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the defaults hash-map would have an empy optional / null. When accessing we test against that. If it is not null/empty, we use that string without localization. If not, we localize a string. The question is, what string? Where do we store the actual default? Storing it where it is accessed is not a good idea IMO.

One solution would be to introduce a 'LocalizablePreference' class that has a boolean flag 'overwrittenByUser' and value. If the flag is set, we read the value from the preferences. If not, it stores the default value. Consumers can then decide whether to localize the value based on the flag.

@calixtus @koppor what do you think?

@tobiasdiez tobiasdiez Sep 4, 2021

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One option would be to have the localized default in the corresponding preference object or in the get* method. The latter is in my opinion anyway a better solution than the huge defaults map.

@koppor

koppor commented Sep 13, 2021

Copy link
Copy Markdown
Member

DevCall impulse: Store only changed values. Do not store default values. - Still unclear how to deal with the localized things. Empty strings appear nice.

@calixtus

Copy link
Copy Markdown
Member

We should make a decision here about how to proceed. Better not the very best decision now as the best decision never. Whatever we decide, we need to move forward here! @JabRef/developers

@koppor

koppor commented Nov 13, 2021

Copy link
Copy Markdown
Member

@calixtus Would you mind to start an ADR here (https://adr.github.io/adr-manager/#)? Then we could decide more easily.

@calixtus

Copy link
Copy Markdown
Member

@btut already created an ADR in this PR docs/adr/0023-localized-preferences.md

koppor and others added 2 commits November 14, 2021 21:00
Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com>
Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com>
Co-authored-by: Benedikt Tutzer <btut@users.noreply.github.com>
@koppor

koppor commented Nov 22, 2021

Copy link
Copy Markdown
Member

We finished ADR-0023 today (https://github.com/JabRef/jabref/blob/unlocalized-preferences/docs/adr/0023-unlocalized-preferences.md).

Follow-up: A new PR will be crafted (as this one has conflicts and the "update" to Java-parser is unnecessary)

@koppor koppor closed this Nov 22, 2021
@btut btut mentioned this pull request Dec 6, 2021
5 tasks
@koppor koppor deleted the unlocalized-preferences branch December 6, 2021 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants