Refactor Localization Service
- Updated LocalizationService interface to load localization data from a Path instead of InputStream. - Enhanced LocalizationServiceImpl to handle loading and saving of localization data from directories. - Introduced LocalizationLanguage and LocalizationMetadata classes to encapsulate language entries and metadata. - Refactored Localizations class to manage language files and metadata more effectively. - Added unit tests for loading localizations from directory and verifying language translations. - Created YAML files for English and German languages along with a metadata file for localization information.
This commit is contained in:
@@ -1016,7 +1016,7 @@ Requires `StaticResourceService` and `StorageService` to be available.
|
||||
### Internationalization (i18n)
|
||||
|
||||
#### Purpose and Overview
|
||||
A YAML-based localization system supporting multiple languages. Localizations are loaded from a structured YAML file containing translation entries organized by language code. The system supports both integer and string-based translation keys and provides fallback to default text when translations are missing.
|
||||
A YAML-based localization system supporting multiple languages. Localizations are loaded from a directory bundle with one metadata file and one file per language, allowing metadata and translation entries to evolve independently. The system supports both integer and string-based translation keys and provides fallback to default text when translations are missing.
|
||||
|
||||
Includes an integrated `LocalizationEditor` - a Swing-based GUI tool for managing translations, adding new languages, and editing localization entries during development.
|
||||
|
||||
@@ -1026,7 +1026,7 @@ Includes an integrated `LocalizationEditor` - a Swing-based GUI tool for managin
|
||||
**Properties**:
|
||||
- `game-engine.i18n.enabled` (default: `true`)
|
||||
- `game-engine.i18n.load-on-start` (default: `true`) - Auto-load localizations on startup
|
||||
- `game-engine.i18n.resource` (default: `i18n/i18n.yaml`) - Path to localization resource
|
||||
- `game-engine.i18n.resource` (default: `i18n/metadata.yaml`) - Path to the localization metadata resource; sibling `*.yaml` files are loaded as individual languages
|
||||
|
||||
#### Exposed Spring Beans
|
||||
- **`LocalizationService`**: Main interface for translation, language management, and localization editing
|
||||
|
||||
13
data/i18n/de.yaml
Normal file
13
data/i18n/de.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
shortName: "de"
|
||||
entries:
|
||||
test-1: "hallo"
|
||||
test-2: "welt"
|
||||
test-3: "Willkommen zu Exo's Spieleengine Demo!"
|
||||
test-4: "Design auswählen"
|
||||
test-5: "Dies ist eine Nachricht\\n123...\\nSchönen Tag noch!"
|
||||
test-6: "Ok"
|
||||
test-7: "Audio umschalten"
|
||||
test-8: "ScrollArea Demo"
|
||||
test-9: "Sprache auswählen"
|
||||
test-10: "Aktuell: "
|
||||
13
data/i18n/en.yaml
Normal file
13
data/i18n/en.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
shortName: "en"
|
||||
entries:
|
||||
test-1: "hello"
|
||||
test-2: "world"
|
||||
test-3: "Welcome to Exo's Game Engine Demo!"
|
||||
test-4: "Select Theme"
|
||||
test-5: "This is a message\\n123...\\nHave a nice day!"
|
||||
test-6: "OK"
|
||||
test-7: "Toggle Audio"
|
||||
test-8: "ScrollArea Demo"
|
||||
test-9: "Select Language"
|
||||
test-10: "Current: "
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
curId: 10
|
||||
descriptions:
|
||||
test-2: ""
|
||||
test-1: "aaa"
|
||||
test-8: ""
|
||||
test-7: ""
|
||||
test-6: ""
|
||||
test-5: ""
|
||||
test-4: ""
|
||||
test-3: ""
|
||||
languages:
|
||||
en:
|
||||
test-1: "hello"
|
||||
test-2: "world"
|
||||
test-3: "Welcome to Exo's Game Engine Demo!"
|
||||
test-4: "Select Theme"
|
||||
test-5: "This is a message\\n123...\\nHave a nice day!"
|
||||
test-6: "OK"
|
||||
test-7: "Toggle Audio"
|
||||
test-8: "ScrollArea Demo"
|
||||
test-9: "Select Language"
|
||||
test-10: "Current: "
|
||||
de:
|
||||
test-1: "hallo"
|
||||
test-2: "welt"
|
||||
test-3: "Willkommen zu Exo's Spieleengine Demo!"
|
||||
test-4: "Design auswählen"
|
||||
test-5: "Dies ist eine Nachricht\\n123...\\nSchönen Tag noch!"
|
||||
test-6: "Ok"
|
||||
test-7: "Audio umschalten"
|
||||
test-8: "ScrollArea Demo"
|
||||
test-9: "Sprache auswählen"
|
||||
test-10: "Aktuell: "
|
||||
languagesInfo:
|
||||
en:
|
||||
name: "English"
|
||||
shortName: "en"
|
||||
de:
|
||||
name: "Deutsch"
|
||||
shortName: "de"
|
||||
prefix: "test"
|
||||
9
data/i18n/metadata.yaml
Normal file
9
data/i18n/metadata.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
languagesInfo:
|
||||
en:
|
||||
name: "English"
|
||||
shortName: "en"
|
||||
de:
|
||||
name: "Deutsch"
|
||||
shortName: "de"
|
||||
prefix: "test"
|
||||
2
pom.xml
2
pom.xml
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.extio</groupId>
|
||||
<artifactId>game-engine</artifactId>
|
||||
<version>1.0.13</version>
|
||||
<version>1.0.14</version>
|
||||
|
||||
<properties>
|
||||
<java.version>25</java.version>
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
package de.extio.game_engine.i18n;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import de.extio.game_engine.resource.StaticResource;
|
||||
import de.extio.game_engine.resource.StaticResourceService;
|
||||
import de.extio.game_engine.storage.StorageService;
|
||||
|
||||
@@ -21,24 +15,13 @@ import de.extio.game_engine.storage.StorageService;
|
||||
@ConditionalOnProperty(name = "game-engine.i18n.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class LocalizationAutoConfiguration {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(LocalizationServiceImpl.class);
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
LocalizationService localizationManager(@Value("${game-engine.i18n.load-on-start:true}") final boolean loadOnStart, @Value("${game-engine.i18n.resource:i18n/i18n.yaml}") final String resource, final StaticResourceService staticResourceService, final StorageService storageService) {
|
||||
final var localizationManager = new LocalizationServiceImpl(storageService);
|
||||
LocalizationService localizationManager(@Value("${game-engine.i18n.load-on-start:true}") final boolean loadOnStart, @Value("${game-engine.i18n.resource:i18n/metadata.yaml}") final String resource, final StaticResourceService staticResourceService, final StorageService storageService) {
|
||||
final var localizationManager = new LocalizationServiceImpl(storageService, staticResourceService);
|
||||
|
||||
if (loadOnStart) {
|
||||
final var path = new File(resource).toPath();
|
||||
final var staticResource = new StaticResource(path.getParent() != null ? StreamSupport.stream(path.getParent().spliterator(), false).map(Path::toString).toList() : null, path.getFileName().toString());
|
||||
staticResourceService.loadStreamByPath(staticResource).ifPresent(stream -> {
|
||||
try (stream) {
|
||||
localizationManager.load(stream);
|
||||
}
|
||||
catch (final Exception e) {
|
||||
LOGGER.error("An exception occured while loading localizations from resource: " + resource, e);
|
||||
}
|
||||
});
|
||||
localizationManager.load(Path.of(resource));
|
||||
}
|
||||
|
||||
return localizationManager;
|
||||
|
||||
@@ -13,13 +13,9 @@ import java.awt.event.ActionListener;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.JButton;
|
||||
@@ -41,8 +37,6 @@ import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
import de.extio.game_engine.util.ObjectSerialization;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class LocalizationEditor extends JFrame {
|
||||
|
||||
@@ -133,7 +127,7 @@ public class LocalizationEditor extends JFrame {
|
||||
* Create the frame.
|
||||
*/
|
||||
public LocalizationEditor() {
|
||||
this.localizationManagerImpl = new LocalizationServiceImpl(null);
|
||||
this.localizationManagerImpl = new LocalizationServiceImpl(null, null);
|
||||
|
||||
this.setTitle("i18n Editor");
|
||||
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
@@ -169,20 +163,9 @@ public class LocalizationEditor extends JFrame {
|
||||
final File cwd = new File(".");
|
||||
final JFileChooser chooser = new JFileChooser();
|
||||
chooser.setCurrentDirectory(cwd);
|
||||
chooser.setFileFilter(new FileFilter() {
|
||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "*.yaml";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(final File f) {
|
||||
return !f.isFile() || f.getName().endsWith("yaml");
|
||||
}
|
||||
});
|
||||
|
||||
final int ret = chooser.showDialog(null, "Select file to import");
|
||||
final int ret = chooser.showDialog(null, "Select localization directory");
|
||||
if (ret == JFileChooser.CANCEL_OPTION) {
|
||||
return;
|
||||
}
|
||||
@@ -191,10 +174,9 @@ public class LocalizationEditor extends JFrame {
|
||||
|
||||
LocalizationEditor.this.localizationManagerImpl.reset();
|
||||
try {
|
||||
LocalizationEditor.this.localizationManagerImpl.reset();
|
||||
LocalizationEditor.this.localizationManagerImpl.load(new FileInputStream(fileName));
|
||||
LocalizationEditor.this.localizationManagerImpl.loadWoService(Path.of(fileName));
|
||||
}
|
||||
catch (final FileNotFoundException e1) {
|
||||
catch (final Exception e1) {
|
||||
JOptionPane.showMessageDialog(null, "An exception occured while loading: " + e1.toString());
|
||||
return;
|
||||
}
|
||||
@@ -239,10 +221,7 @@ public class LocalizationEditor extends JFrame {
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
try {
|
||||
LocalizationEditor.this.localizationManagerImpl.getLocalizations().setPrefix(LocalizationEditor.this.txtTextprefix.getText());
|
||||
|
||||
try (OutputStream output = new FileOutputStream(LocalizationEditor.this.lastFileName)) {
|
||||
ObjectSerialization.serialize(LocalizationEditor.this.localizationManagerImpl.getLocalizations(), output, false, false, false, null, null);
|
||||
}
|
||||
LocalizationEditor.this.localizationManagerImpl.save(Path.of(LocalizationEditor.this.lastFileName));
|
||||
}
|
||||
catch (final Exception exc) {
|
||||
JOptionPane.showMessageDialog(null, "An exception occured while saving: " + exc.toString());
|
||||
@@ -263,18 +242,7 @@ public class LocalizationEditor extends JFrame {
|
||||
final File cwd = new File(".");
|
||||
final JFileChooser chooser = new JFileChooser();
|
||||
chooser.setCurrentDirectory(cwd);
|
||||
chooser.setFileFilter(new FileFilter() {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "*.yaml";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(final File f) {
|
||||
return !f.isFile() || f.getName().endsWith("yaml");
|
||||
}
|
||||
});
|
||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
|
||||
final int ret = chooser.showSaveDialog(null);
|
||||
if (ret == JFileChooser.CANCEL_OPTION) {
|
||||
@@ -286,9 +254,7 @@ public class LocalizationEditor extends JFrame {
|
||||
LocalizationEditor.this.localizationManagerImpl.getLocalizations().setPrefix(LocalizationEditor.this.txtTextprefix.getText());
|
||||
|
||||
try {
|
||||
try (OutputStream output = new FileOutputStream(fileName)) {
|
||||
ObjectSerialization.serialize(LocalizationEditor.this.localizationManagerImpl.getLocalizations(), output, false, false, false, null, null);
|
||||
}
|
||||
LocalizationEditor.this.localizationManagerImpl.save(Path.of(fileName));
|
||||
}
|
||||
catch (final Exception exc) {
|
||||
JOptionPane.showMessageDialog(null, "An exception occured while saving: " + exc.toString());
|
||||
@@ -339,7 +305,7 @@ public class LocalizationEditor extends JFrame {
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
final String idStr = JOptionPane.showInputDialog(LocalizationEditor.this, "Enter localization id");
|
||||
|
||||
final Map<String, String> mapping = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguages().values().iterator().next();
|
||||
final Map<String, String> mapping = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguageFiles().values().iterator().next().getEntries();
|
||||
int idx = 0;
|
||||
for (final String mid : mapping.keySet()) {
|
||||
if (mid.equals(idStr)) {
|
||||
@@ -361,10 +327,10 @@ public class LocalizationEditor extends JFrame {
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
final String search = JOptionPane.showInputDialog(LocalizationEditor.this, "Enter search term");
|
||||
|
||||
final Iterator<Map<String, String>> it = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguages().values().iterator();
|
||||
final Iterator<LocalizationLanguage> it = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguageFiles().values().iterator();
|
||||
while (it.hasNext()) {
|
||||
int idx = 0;
|
||||
for (final String text : it.next().values()) {
|
||||
for (final String text : it.next().getEntries().values()) {
|
||||
if (text.toLowerCase().contains(search.toLowerCase())) {
|
||||
LocalizationEditor.this.table.changeSelection(idx, 0, false, false);
|
||||
LocalizationEditor.this.table.scrollRectToVisible(LocalizationEditor.this.table.getCellRect(idx, 0, true));
|
||||
@@ -454,7 +420,7 @@ public class LocalizationEditor extends JFrame {
|
||||
}
|
||||
|
||||
for (final Language lang : LocalizationEditor.this.localizationManagerImpl.getLanguages()) {
|
||||
final String value = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguages().get(lang.getShortName()).get(oldId);
|
||||
final String value = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguageEntries(lang.getShortName()).get(oldId);
|
||||
LocalizationEditor.this.localizationManagerImpl.put(lang.getShortName(), idStr, value);
|
||||
}
|
||||
|
||||
@@ -490,8 +456,9 @@ public class LocalizationEditor extends JFrame {
|
||||
|
||||
@Override
|
||||
public Object getValueAt(final int rowIndex, final int columnIndex) {
|
||||
final String id = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguages().values().stream()
|
||||
final String id = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguageFiles().values().stream()
|
||||
.findFirst()
|
||||
.map(LocalizationLanguage::getEntries)
|
||||
.map(mapping -> mapping.keySet().stream().skip(rowIndex).findFirst().orElse("-1"))
|
||||
.orElse("-1");
|
||||
if (columnIndex == 0) {
|
||||
@@ -499,9 +466,9 @@ public class LocalizationEditor extends JFrame {
|
||||
}
|
||||
|
||||
final String lang = LocalizationEditor.this.localizationManagerImpl.getLanguages().get(columnIndex - 1).getShortName();
|
||||
final String value = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguages().entrySet().stream()
|
||||
final String value = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguageFiles().entrySet().stream()
|
||||
.filter(entry -> entry.getKey().equals(lang))
|
||||
.map(entry -> entry.getValue().getOrDefault(id, ""))
|
||||
.map(entry -> entry.getValue().getEntries().getOrDefault(id, ""))
|
||||
.findFirst()
|
||||
.orElse("");
|
||||
return value;
|
||||
@@ -509,7 +476,7 @@ public class LocalizationEditor extends JFrame {
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguages().values().stream().findFirst().map(mapping -> Integer.valueOf(mapping.size())).orElse(Integer.valueOf(0));
|
||||
return LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguageFiles().values().stream().findFirst().map(LocalizationLanguage::getEntries).map(mapping -> Integer.valueOf(mapping.size())).orElse(Integer.valueOf(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -533,15 +500,16 @@ public class LocalizationEditor extends JFrame {
|
||||
|
||||
@Override
|
||||
public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) {
|
||||
final String id = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguages().values().stream()
|
||||
final String id = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguageFiles().values().stream()
|
||||
.findFirst()
|
||||
.map(LocalizationLanguage::getEntries)
|
||||
.map(mapping -> mapping.keySet().stream().skip(rowIndex).findFirst().orElse(null))
|
||||
.orElse(null);
|
||||
if (id == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String langShortName = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguages().keySet().stream().skip(columnIndex - 1).findFirst().orElse(null);
|
||||
final String langShortName = LocalizationEditor.this.localizationManagerImpl.getLocalizations().getLanguageFiles().keySet().stream().skip(columnIndex - 1).findFirst().orElse(null);
|
||||
if (langShortName == null) {
|
||||
return;
|
||||
}
|
||||
@@ -572,7 +540,7 @@ public class LocalizationEditor extends JFrame {
|
||||
}
|
||||
final Language result = new Language(name, lang);
|
||||
this.localizationManagerImpl.getLocalizations().getLanguagesInfo().put(result.getShortName(), result);
|
||||
this.localizationManagerImpl.getLocalizations().getLanguages().put(result.getShortName(), new LinkedHashMap<>());
|
||||
this.localizationManagerImpl.getLocalizations().ensureLanguageFile(result.getShortName());
|
||||
|
||||
((AbstractTableModel) this.table.getModel()).fireTableStructureChanged();
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package de.extio.game_engine.i18n;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class LocalizationLanguage {
|
||||
|
||||
private String shortName;
|
||||
|
||||
private Map<String, String> entries = new LinkedHashMap<>();
|
||||
|
||||
public LocalizationLanguage() {
|
||||
|
||||
}
|
||||
|
||||
public LocalizationLanguage(final String shortName) {
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
public LocalizationLanguage(final LocalizationLanguage other) {
|
||||
this.shortName = other.shortName;
|
||||
this.entries = new LinkedHashMap<>(other.entries);
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return this.shortName;
|
||||
}
|
||||
|
||||
public void setShortName(final String shortName) {
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
public Map<String, String> getEntries() {
|
||||
return this.entries;
|
||||
}
|
||||
|
||||
public void setEntries(final Map<String, String> entries) {
|
||||
this.entries = new LinkedHashMap<>(entries);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package de.extio.game_engine.i18n;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public final class LocalizationMetadata {
|
||||
|
||||
private String prefix;
|
||||
|
||||
private Map<String, Language> languagesInfo = new LinkedHashMap<>();
|
||||
|
||||
public LocalizationMetadata() {
|
||||
|
||||
}
|
||||
|
||||
public LocalizationMetadata(final LocalizationMetadata other) {
|
||||
this.prefix = other.prefix;
|
||||
this.languagesInfo = new LinkedHashMap<>();
|
||||
for (final Entry<String, Language> entry : other.languagesInfo.entrySet()) {
|
||||
this.languagesInfo.put(entry.getKey(), new Language(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(final String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public Map<String, Language> getLanguagesInfo() {
|
||||
return this.languagesInfo;
|
||||
}
|
||||
|
||||
public void setLanguagesInfo(final Map<String, Language> languagesInfo) {
|
||||
this.languagesInfo = new LinkedHashMap<>();
|
||||
for (final Entry<String, Language> entry : languagesInfo.entrySet()) {
|
||||
this.languagesInfo.put(entry.getKey(), new Language(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.extio.game_engine.i18n;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public interface LocalizationService {
|
||||
@@ -9,7 +10,7 @@ public interface LocalizationService {
|
||||
|
||||
void reset();
|
||||
|
||||
void load(InputStream stream);
|
||||
void load(Path metaDataPath);
|
||||
|
||||
void setLanguage(String lang);
|
||||
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
package de.extio.game_engine.i18n;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.extio.game_engine.resource.StaticResource;
|
||||
import de.extio.game_engine.resource.StaticResourceService;
|
||||
import de.extio.game_engine.storage.StorageService;
|
||||
import de.extio.game_engine.util.ObjectSerialization;
|
||||
|
||||
@@ -20,25 +29,35 @@ public class LocalizationServiceImpl implements LocalizationService {
|
||||
|
||||
public final static String NOT_FOUND_PREFIX = "{i18n.";
|
||||
|
||||
public static final String METADATA_FILE_NAME = "metadata.yaml";
|
||||
|
||||
public static final String LANGUAGE_FILE_SUFFIX = ".yaml";
|
||||
|
||||
private static final List<String> LANGUAGE_STORAGE_PATH = List.of("gameEngine");
|
||||
|
||||
private static final String LANGUAGE_STORAGE_NAME = "currentLanguage";
|
||||
|
||||
private final StorageService storageService;
|
||||
|
||||
private final StaticResourceService staticResourceService;
|
||||
|
||||
private Localizations localizations = new Localizations();
|
||||
|
||||
private Map<String, String> currentLanguage;
|
||||
|
||||
private String currentLanguageName;
|
||||
|
||||
LocalizationServiceImpl(final StorageService storageService) {
|
||||
private int nextId = 1;
|
||||
|
||||
LocalizationServiceImpl(final StorageService storageService, final StaticResourceService staticResourceService) {
|
||||
this.storageService = storageService;
|
||||
this.staticResourceService = staticResourceService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetEntries() {
|
||||
this.localizations.getLanguages().forEach((lang, mapping) -> mapping.clear());
|
||||
this.localizations.getLanguageFiles().values().forEach(language -> language.getEntries().clear());
|
||||
this.nextId = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,68 +65,131 @@ public class LocalizationServiceImpl implements LocalizationService {
|
||||
this.localizations = new Localizations();
|
||||
this.currentLanguage = null;
|
||||
this.currentLanguageName = null;
|
||||
this.nextId = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(final InputStream stream) {
|
||||
Localizations localizations;
|
||||
try {
|
||||
localizations = ObjectSerialization.deserialize(Localizations.class, stream, false, false, null, null, null);
|
||||
public void load(final Path metaDataPath) {
|
||||
final List<String> resourcePath = metaDataPath.getParent() == null ? List.of() : StreamSupport.stream(metaDataPath.getParent().spliterator(), false).map(Path::toString).toList();
|
||||
final var metadataFileName = metaDataPath.getFileName().toString();
|
||||
final var staticResource = new StaticResource(resourcePath.isEmpty() ? null : resourcePath, metadataFileName);
|
||||
staticResourceService.loadStreamByPath(staticResource).ifPresent(stream -> {
|
||||
try (stream) {
|
||||
final var metadata = ObjectSerialization.deserialize(LocalizationMetadata.class, stream, false, false, null, null, null);
|
||||
final var languages = new ArrayList<LocalizationLanguage>();
|
||||
for (final String fileName : staticResourceService.listPath(resourcePath)) {
|
||||
if (metadataFileName.equals(fileName) || !fileName.endsWith(LANGUAGE_FILE_SUFFIX)) {
|
||||
continue;
|
||||
}
|
||||
catch (final Exception exc) {
|
||||
LOGGER.error("An exception occured while loading localizations", exc);
|
||||
return;
|
||||
staticResourceService.loadStreamByPath(resourcePath, fileName).ifPresent(languageStream -> {
|
||||
try (languageStream) {
|
||||
final var language = ObjectSerialization.deserialize(LocalizationLanguage.class, languageStream, false, false, null, null, null);
|
||||
if (language != null) {
|
||||
languages.add(language);
|
||||
}
|
||||
}
|
||||
catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.reset();
|
||||
this.load(metadata, languages);
|
||||
}
|
||||
catch (final Exception e) {
|
||||
LOGGER.error("An exception occured while loading localizations from resource: " + metaDataPath, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (localizations != null) {
|
||||
localizations.getLanguages().forEach((lang, newMapping) -> {
|
||||
final Map<String, String> localizationEntries = this.localizations.getLanguages().computeIfAbsent(lang, k -> new LinkedHashMap<>());
|
||||
for (final Entry<String, String> newEntry : newMapping.entrySet()) {
|
||||
void loadWoService(final Path directory) throws IOException {
|
||||
Objects.requireNonNull(directory, "directory");
|
||||
this.reset();
|
||||
try (final var metadataStream = Files.newInputStream(directory.resolve(METADATA_FILE_NAME))) {
|
||||
final var metadata = ObjectSerialization.deserialize(LocalizationMetadata.class, metadataStream, false, false, null, null, null);
|
||||
final List<LocalizationLanguage> languages;
|
||||
try (final Stream<Path> files = Files.list(directory)) {
|
||||
languages = files
|
||||
.filter(Files::isRegularFile)
|
||||
.map(Path::getFileName)
|
||||
.map(Path::toString)
|
||||
.filter(fileName -> !METADATA_FILE_NAME.equals(fileName) && fileName.endsWith(LANGUAGE_FILE_SUFFIX))
|
||||
.sorted()
|
||||
.map(fileName -> this.loadLanguage(directory.resolve(fileName)))
|
||||
.toList();
|
||||
}
|
||||
this.load(metadata, languages);
|
||||
}
|
||||
}
|
||||
|
||||
void load(final LocalizationMetadata metadata, final List<LocalizationLanguage> languages) {
|
||||
if (metadata != null) {
|
||||
this.localizations.setPrefix(metadata.getPrefix());
|
||||
metadata.getLanguagesInfo().forEach((shortName, language) -> this.localizations.getLanguagesInfo().put(shortName, new Language(language)));
|
||||
this.localizations.getLanguagesInfo().keySet().forEach(this.localizations::ensureLanguageFile);
|
||||
}
|
||||
for (final LocalizationLanguage language : languages) {
|
||||
if (language == null || language.getShortName() == null || language.getShortName().isBlank()) {
|
||||
continue;
|
||||
}
|
||||
final var localizationEntries = this.localizations.ensureLanguageFile(language.getShortName()).getEntries();
|
||||
for (final Entry<String, String> newEntry : language.getEntries().entrySet()) {
|
||||
if (!localizationEntries.containsKey(newEntry.getKey()) || (newEntry.getValue() != null && !newEntry.getValue().isEmpty())) {
|
||||
localizationEntries.put(newEntry.getKey(), newEntry.getValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
this.localizations.getLanguagesInfo().putAll(localizations.getLanguagesInfo());
|
||||
|
||||
int maxId = 0;
|
||||
for (final Map<String, String> entries : this.localizations.getLanguages().values()) {
|
||||
for (final String locId : entries.keySet()) {
|
||||
final int intId = parseNumericId(locId);
|
||||
if (intId > -1) {
|
||||
maxId = Math.max(maxId, intId);
|
||||
this.localizations.getLanguagesInfo().computeIfAbsent(language.getShortName(), shortName -> new Language(shortName, shortName));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.localizations.setCurId(maxId);
|
||||
|
||||
this.localizations.setPrefix(localizations.getPrefix());
|
||||
|
||||
LOGGER.info("Loaded localizations for {} languages", localizations.getLanguages().size());
|
||||
}
|
||||
|
||||
this.recalculateNextId();
|
||||
LOGGER.info("Loaded localizations for {} languages", this.localizations.getLanguageFiles().size());
|
||||
this.loadPersistedLanguageName();
|
||||
if (this.currentLanguageName == null) {
|
||||
this.currentLanguageName = "en";
|
||||
}
|
||||
this.setLanguage(this.currentLanguageName);
|
||||
}
|
||||
|
||||
public void save(final Path directory) throws IOException {
|
||||
Objects.requireNonNull(directory, "directory");
|
||||
Files.createDirectories(directory);
|
||||
try (final var metadataStream = Files.newOutputStream(directory.resolve(METADATA_FILE_NAME))) {
|
||||
ObjectSerialization.serialize(this.localizations.getMetadata(), metadataStream, false, false, false, null, null);
|
||||
}
|
||||
final var expectedFiles = new HashSet<String>();
|
||||
expectedFiles.add(METADATA_FILE_NAME);
|
||||
for (final LocalizationLanguage language : this.localizations.getLanguageFiles().values()) {
|
||||
if (language.getShortName() == null || language.getShortName().isBlank()) {
|
||||
continue;
|
||||
}
|
||||
final var fileName = languageFileName(language.getShortName());
|
||||
expectedFiles.add(fileName);
|
||||
try (final var languageStream = Files.newOutputStream(directory.resolve(fileName))) {
|
||||
ObjectSerialization.serialize(language, languageStream, false, false, false, null, null);
|
||||
}
|
||||
}
|
||||
try (final Stream<Path> files = Files.list(directory)) {
|
||||
for (final Path file : files.filter(Files::isRegularFile).toList()) {
|
||||
final var fileName = file.getFileName().toString();
|
||||
if (fileName.endsWith(LANGUAGE_FILE_SUFFIX) && !expectedFiles.contains(fileName)) {
|
||||
Files.deleteIfExists(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLanguage(final String lang) {
|
||||
if (!this.localizations.getLanguages().containsKey(lang)) {
|
||||
if (this.localizations.getLanguageFile(lang) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentLanguageName = lang;
|
||||
this.currentLanguage = this.localizations.getLanguages().get(lang);
|
||||
this.currentLanguage = this.localizations.getLanguageEntries(lang);
|
||||
this.persistCurrentLanguageName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Language> getLanguages() {
|
||||
if (this.localizations.getLanguagesInfo() == null || this.localizations.getLanguagesInfo().isEmpty()) {
|
||||
if (this.localizations.getLanguagesInfo().isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
return List.copyOf(this.localizations.getLanguagesInfo().values());
|
||||
@@ -157,12 +239,13 @@ public class LocalizationServiceImpl implements LocalizationService {
|
||||
|
||||
@Override
|
||||
public void put(final String lang, final String id, final String value) {
|
||||
final Map<String, String> mapping = this.localizations.getLanguages().get(lang);
|
||||
final Map<String, String> mapping = this.localizations.getLanguageEntries(lang);
|
||||
if (mapping == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mapping.put(id, value);
|
||||
this.updateNextId(id);
|
||||
if (!this.localizations.getLanguagesInfo().containsKey(lang)) {
|
||||
this.localizations.getLanguagesInfo().put(lang, new Language(lang, lang));
|
||||
}
|
||||
@@ -170,13 +253,14 @@ public class LocalizationServiceImpl implements LocalizationService {
|
||||
|
||||
@Override
|
||||
public void remove(final String id) {
|
||||
this.localizations.getLanguages().forEach((k, v) -> v.remove(id));
|
||||
this.localizations.getLanguageFiles().values().forEach(language -> language.getEntries().remove(id));
|
||||
this.recalculateNextId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getNextId() {
|
||||
final int result = this.localizations.getCurId() + 1;
|
||||
this.localizations.setCurId(result);
|
||||
final int result = this.nextId;
|
||||
this.nextId++;
|
||||
return Integer.valueOf(result);
|
||||
}
|
||||
|
||||
@@ -204,6 +288,39 @@ public class LocalizationServiceImpl implements LocalizationService {
|
||||
}
|
||||
}
|
||||
|
||||
public static String languageFileName(final String shortName) {
|
||||
return shortName + LANGUAGE_FILE_SUFFIX;
|
||||
}
|
||||
|
||||
private LocalizationLanguage loadLanguage(final Path file) {
|
||||
try (final var languageStream = Files.newInputStream(file)) {
|
||||
return ObjectSerialization.deserialize(LocalizationLanguage.class, languageStream, false, false, null, null, null);
|
||||
}
|
||||
catch (final Exception e) {
|
||||
throw new RuntimeException("Could not load localization language file " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void recalculateNextId() {
|
||||
int maxId = 0;
|
||||
for (final LocalizationLanguage language : this.localizations.getLanguageFiles().values()) {
|
||||
for (final String locId : language.getEntries().keySet()) {
|
||||
final int intId = parseNumericId(locId);
|
||||
if (intId > -1) {
|
||||
maxId = Math.max(maxId, intId);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.nextId = maxId + 1;
|
||||
}
|
||||
|
||||
private void updateNextId(final String id) {
|
||||
final int numericId = parseNumericId(id);
|
||||
if (numericId > -1) {
|
||||
this.nextId = Math.max(this.nextId, numericId + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void persistCurrentLanguageName() {
|
||||
if (this.storageService == null) {
|
||||
return;
|
||||
|
||||
@@ -9,61 +9,64 @@ import java.util.Map.Entry;
|
||||
*/
|
||||
public final class Localizations {
|
||||
|
||||
private String prefix;
|
||||
private LocalizationMetadata metadata = new LocalizationMetadata();
|
||||
|
||||
private Map<String, Map<String, String>> languages = new LinkedHashMap<>();
|
||||
|
||||
private Map<String, Language> languagesInfo = new LinkedHashMap<>();
|
||||
|
||||
private int curId;
|
||||
private Map<String, LocalizationLanguage> languageFiles = new LinkedHashMap<>();
|
||||
|
||||
public Localizations() {
|
||||
|
||||
}
|
||||
|
||||
public Localizations(final Localizations other) {
|
||||
this.languages = new LinkedHashMap<>();
|
||||
for (final Entry<String, Map<String, String>> entry : other.languages.entrySet()) {
|
||||
this.languages.put(entry.getKey(), new LinkedHashMap<>(entry.getValue()));
|
||||
this.metadata = new LocalizationMetadata(other.metadata);
|
||||
this.languageFiles = new LinkedHashMap<>();
|
||||
for (final Entry<String, LocalizationLanguage> entry : other.languageFiles.entrySet()) {
|
||||
this.languageFiles.put(entry.getKey(), new LocalizationLanguage(entry.getValue()));
|
||||
}
|
||||
this.languagesInfo = new LinkedHashMap<>();
|
||||
for (final Entry<String, Language> entry : other.languagesInfo.entrySet()) {
|
||||
this.languagesInfo.put(entry.getKey(), new Language(entry.getValue()));
|
||||
}
|
||||
this.curId = other.curId;
|
||||
this.prefix = other.prefix;
|
||||
}
|
||||
|
||||
public Map<String, Map<String, String>> getLanguages() {
|
||||
return this.languages;
|
||||
public LocalizationMetadata getMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
public void setLanguages(final Map<String, Map<String, String>> languages) {
|
||||
this.languages = new LinkedHashMap<>(languages);
|
||||
public void setMetadata(final LocalizationMetadata metadata) {
|
||||
this.metadata = metadata == null ? new LocalizationMetadata() : new LocalizationMetadata(metadata);
|
||||
}
|
||||
|
||||
public Map<String, Language> getLanguagesInfo() {
|
||||
return this.languagesInfo;
|
||||
public Map<String, LocalizationLanguage> getLanguageFiles() {
|
||||
return this.languageFiles;
|
||||
}
|
||||
|
||||
public void setLanguagesInfo(final Map<String, Language> languagesInfo) {
|
||||
this.languagesInfo = new LinkedHashMap<>(languagesInfo);
|
||||
public void setLanguageFiles(final Map<String, LocalizationLanguage> languageFiles) {
|
||||
this.languageFiles = new LinkedHashMap<>();
|
||||
for (final Entry<String, LocalizationLanguage> entry : languageFiles.entrySet()) {
|
||||
this.languageFiles.put(entry.getKey(), new LocalizationLanguage(entry.getValue()));
|
||||
}
|
||||
|
||||
public int getCurId() {
|
||||
return this.curId;
|
||||
}
|
||||
|
||||
public void setCurId(final int curId) {
|
||||
this.curId = curId;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
return this.metadata.getPrefix();
|
||||
}
|
||||
|
||||
public void setPrefix(final String prefix) {
|
||||
this.prefix = prefix;
|
||||
this.metadata.setPrefix(prefix);
|
||||
}
|
||||
|
||||
public Map<String, Language> getLanguagesInfo() {
|
||||
return this.metadata.getLanguagesInfo();
|
||||
}
|
||||
|
||||
public LocalizationLanguage getLanguageFile(final String shortName) {
|
||||
return this.languageFiles.get(shortName);
|
||||
}
|
||||
|
||||
public LocalizationLanguage ensureLanguageFile(final String shortName) {
|
||||
return this.languageFiles.computeIfAbsent(shortName, key -> new LocalizationLanguage(key));
|
||||
}
|
||||
|
||||
public Map<String, String> getLanguageEntries(final String shortName) {
|
||||
final var languageFile = this.getLanguageFile(shortName);
|
||||
return languageFile == null ? null : languageFile.getEntries();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package de.extio.game_engine.i18n;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -14,12 +14,14 @@ import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import de.extio.game_engine.resource.StaticResourceService;
|
||||
import de.extio.game_engine.storage.StorageService;
|
||||
|
||||
class LocalizationManagerTest {
|
||||
@@ -27,12 +29,21 @@ class LocalizationManagerTest {
|
||||
@Mock
|
||||
private StorageService storageService;
|
||||
|
||||
private LocalizationService localizationManager;
|
||||
@Mock
|
||||
private StaticResourceService staticResourceService;
|
||||
|
||||
private LocalizationServiceImpl localizationManager;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
this.localizationManager = new LocalizationServiceImpl(storageService);
|
||||
this.localizationManager = new LocalizationServiceImpl(storageService, staticResourceService);
|
||||
}
|
||||
|
||||
private static LocalizationLanguage language(final String shortName, final Map<String, String> entries) {
|
||||
final var language = new LocalizationLanguage(shortName);
|
||||
language.setEntries(new LinkedHashMap<>(entries));
|
||||
return language;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -44,40 +55,31 @@ class LocalizationManagerTest {
|
||||
|
||||
@Test
|
||||
void testResetEntries() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
this.localizationManager.resetEntries();
|
||||
|
||||
assertTrue(localizations.getLanguages().get("en").isEmpty());
|
||||
assertTrue(localizations.getLanguageEntries("en").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadLocalizations() {
|
||||
Localizations localizationsToSave = new Localizations();
|
||||
Language english = new Language("English", "en");
|
||||
Map<String, String> englishEntries = new LinkedHashMap<>(Map.of("greeting", "Hello", "farewell", "Goodbye"));
|
||||
localizationsToSave.getLanguagesInfo().put("en", english);
|
||||
localizationsToSave.getLanguages().put("en", englishEntries);
|
||||
localizationsToSave.setPrefix("i18n.");
|
||||
localizationsToSave.setCurId(1);
|
||||
final LocalizationMetadata metadata = new LocalizationMetadata();
|
||||
final Language english = new Language("English", "en");
|
||||
final Map<String, String> englishEntries = new LinkedHashMap<>(Map.of("greeting", "Hello", "farewell", "Goodbye"));
|
||||
metadata.getLanguagesInfo().put("en", english);
|
||||
metadata.setPrefix("i18n.");
|
||||
this.localizationManager.load(metadata, List.of(language("en", englishEntries)));
|
||||
|
||||
byte[] serialized = de.extio.game_engine.util.ObjectSerialization.serialize(
|
||||
localizationsToSave, false, false, false, null, digest -> {
|
||||
});
|
||||
|
||||
InputStream stream = new ByteArrayInputStream(serialized);
|
||||
|
||||
this.localizationManager.load(stream);
|
||||
|
||||
Localizations loaded = this.localizationManager.getLocalizations();
|
||||
final Localizations loaded = this.localizationManager.getLocalizations();
|
||||
assertNotNull(loaded);
|
||||
assertEquals("i18n.", loaded.getPrefix());
|
||||
|
||||
List<Language> languages = this.localizationManager.getLanguages();
|
||||
final List<Language> languages = this.localizationManager.getLanguages();
|
||||
assertEquals(1, languages.size());
|
||||
assertEquals("en", languages.getFirst().getShortName());
|
||||
|
||||
@@ -86,15 +88,33 @@ class LocalizationManagerTest {
|
||||
assertEquals("Goodbye", this.localizationManager.translate("farewell"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadLocalizationsFromDirectory(@TempDir final Path directory) throws Exception {
|
||||
final LocalizationMetadata metadata = new LocalizationMetadata();
|
||||
metadata.getLanguagesInfo().put("en", new Language("English", "en"));
|
||||
metadata.getLanguagesInfo().put("de", new Language("German", "de"));
|
||||
metadata.setPrefix("i18n.");
|
||||
Files.write(directory.resolve(LocalizationServiceImpl.METADATA_FILE_NAME), de.extio.game_engine.util.ObjectSerialization.serialize(metadata, false, false, false, null, digest -> {}));
|
||||
Files.write(directory.resolve(LocalizationServiceImpl.languageFileName("en")), de.extio.game_engine.util.ObjectSerialization.serialize(language("en", Map.of("i18n.greeting", "Hello")), false, false, false, null, digest -> {}));
|
||||
Files.write(directory.resolve(LocalizationServiceImpl.languageFileName("de")), de.extio.game_engine.util.ObjectSerialization.serialize(language("de", Map.of("i18n.greeting", "Hallo")), false, false, false, null, digest -> {}));
|
||||
|
||||
this.localizationManager.loadWoService(directory);
|
||||
|
||||
// this.localizationManager.setLanguage("en");
|
||||
assertEquals("Hello", this.localizationManager.translate("i18n.greeting"));
|
||||
this.localizationManager.setLanguage("de");
|
||||
assertEquals("Hallo", this.localizationManager.translate("i18n.greeting"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetLanguage() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
Language german = new Language("German", "de");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
final Language german = new Language("German", "de");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguagesInfo().put("de", german);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.getLanguages().put("de", new LinkedHashMap<>(Map.of("greeting", "Hallo")));
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.ensureLanguageFile("de").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hallo")));
|
||||
|
||||
this.localizationManager.setLanguage("de");
|
||||
|
||||
@@ -103,10 +123,10 @@ class LocalizationManagerTest {
|
||||
|
||||
@Test
|
||||
void testSetLanguageInvalid() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
this.localizationManager.setLanguage("fr");
|
||||
@@ -116,15 +136,15 @@ class LocalizationManagerTest {
|
||||
|
||||
@Test
|
||||
void testGetLanguages() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
Language german = new Language("German", "de");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
final Language german = new Language("German", "de");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguagesInfo().put("de", german);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.getLanguages().put("de", new LinkedHashMap<>(Map.of("greeting", "Hallo")));
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.ensureLanguageFile("de").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hallo")));
|
||||
|
||||
List<Language> languages = this.localizationManager.getLanguages();
|
||||
final List<Language> languages = this.localizationManager.getLanguages();
|
||||
|
||||
assertEquals(2, languages.size());
|
||||
assertTrue(languages.stream().anyMatch(lang -> "en".equals(lang.getShortName())));
|
||||
@@ -133,10 +153,10 @@ class LocalizationManagerTest {
|
||||
|
||||
@Test
|
||||
void testGetCurrentLanguage() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
assertEquals("en", this.localizationManager.getCurrentLanguage());
|
||||
@@ -144,103 +164,100 @@ class LocalizationManagerTest {
|
||||
|
||||
@Test
|
||||
void testTranslateWithStringId() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
String result = this.localizationManager.translate("greeting");
|
||||
final String result = this.localizationManager.translate("greeting");
|
||||
|
||||
assertEquals("Hello", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTranslateWithStringIdNotFound() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>());
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>());
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
String result = this.localizationManager.translate("unknown");
|
||||
final String result = this.localizationManager.translate("unknown");
|
||||
|
||||
assertEquals("unknown", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTranslateWithStringIdAndDefault() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>());
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>());
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
String result = this.localizationManager.translate("unknown", "Default Text");
|
||||
final String result = this.localizationManager.translate("unknown", "Default Text");
|
||||
|
||||
assertEquals("Default Text", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPut() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>());
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>());
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
this.localizationManager.put("en", "greeting", "Hello");
|
||||
String result = this.localizationManager.translate("greeting");
|
||||
final String result = this.localizationManager.translate("greeting");
|
||||
|
||||
assertEquals("Hello", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPutInvalidLanguage() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>());
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>());
|
||||
|
||||
this.localizationManager.put("fr", "greeting", "Bonjour");
|
||||
|
||||
assertTrue(localizations.getLanguages().get("en").isEmpty());
|
||||
assertTrue(localizations.getLanguageEntries("en").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemove() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
Map<String, String> entries = new LinkedHashMap<>(Map.of("greeting", "Hello", "farewell", "Goodbye"));
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
final Map<String, String> entries = new LinkedHashMap<>(Map.of("greeting", "Hello", "farewell", "Goodbye"));
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", entries);
|
||||
localizations.ensureLanguageFile("en").setEntries(entries);
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
this.localizationManager.remove("greeting");
|
||||
|
||||
assertFalse(localizations.getLanguages().get("en").containsKey("greeting"));
|
||||
assertTrue(localizations.getLanguages().get("en").containsKey("farewell"));
|
||||
assertFalse(localizations.getLanguageEntries("en").containsKey("greeting"));
|
||||
assertTrue(localizations.getLanguageEntries("en").containsKey("farewell"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetNextId() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
localizations.setCurId(5);
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("test-5", "Hello")));
|
||||
this.localizationManager.load(localizations.getMetadata(), List.of(localizations.getLanguageFile("en")));
|
||||
|
||||
Integer nextId = this.localizationManager.getNextId();
|
||||
final Integer nextId = this.localizationManager.getNextId();
|
||||
|
||||
assertEquals(6, nextId);
|
||||
assertEquals(6, localizations.getCurId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetNextIdIncremental() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
localizations.setCurId(0);
|
||||
|
||||
Integer id1 = this.localizationManager.getNextId();
|
||||
Integer id2 = this.localizationManager.getNextId();
|
||||
Integer id3 = this.localizationManager.getNextId();
|
||||
final Integer id1 = this.localizationManager.getNextId();
|
||||
final Integer id2 = this.localizationManager.getNextId();
|
||||
final Integer id3 = this.localizationManager.getNextId();
|
||||
|
||||
assertEquals(1, id1);
|
||||
assertEquals(2, id2);
|
||||
@@ -249,7 +266,7 @@ class LocalizationManagerTest {
|
||||
|
||||
@Test
|
||||
void testGetLocalizations() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
|
||||
assertNotNull(localizations);
|
||||
assertEquals(localizations, this.localizationManager.getLocalizations());
|
||||
@@ -257,14 +274,14 @@ class LocalizationManagerTest {
|
||||
|
||||
@Test
|
||||
void testTranslateCaching() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("1", "Hello")));
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("1", "Hello")));
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
String result1 = this.localizationManager.translate("1");
|
||||
String result2 = this.localizationManager.translate("1");
|
||||
final String result1 = this.localizationManager.translate("1");
|
||||
final String result2 = this.localizationManager.translate("1");
|
||||
|
||||
assertEquals("Hello", result1);
|
||||
assertEquals("Hello", result2);
|
||||
@@ -273,19 +290,19 @@ class LocalizationManagerTest {
|
||||
|
||||
@Test
|
||||
void testMultipleLanguages() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
Language german = new Language("German", "de");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
final Language german = new Language("German", "de");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguagesInfo().put("de", german);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.getLanguages().put("de", new LinkedHashMap<>(Map.of("greeting", "Hallo")));
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.ensureLanguageFile("de").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hallo")));
|
||||
|
||||
this.localizationManager.setLanguage("en");
|
||||
String englishGreeting = this.localizationManager.translate("greeting");
|
||||
final String englishGreeting = this.localizationManager.translate("greeting");
|
||||
|
||||
this.localizationManager.setLanguage("de");
|
||||
String germanGreeting = this.localizationManager.translate("greeting");
|
||||
final String germanGreeting = this.localizationManager.translate("greeting");
|
||||
|
||||
assertEquals("Hello", englishGreeting);
|
||||
assertEquals("Hallo", germanGreeting);
|
||||
@@ -293,10 +310,10 @@ class LocalizationManagerTest {
|
||||
|
||||
@Test
|
||||
void testPersistCurrentLanguage() {
|
||||
Localizations localizations = this.localizationManager.getLocalizations();
|
||||
Language english = new Language("English", "en");
|
||||
final Localizations localizations = this.localizationManager.getLocalizations();
|
||||
final Language english = new Language("English", "en");
|
||||
localizations.getLanguagesInfo().put("en", english);
|
||||
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
localizations.ensureLanguageFile("en").setEntries(new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||
|
||||
this.localizationManager.setLanguage("en");
|
||||
|
||||
@@ -308,20 +325,10 @@ class LocalizationManagerTest {
|
||||
when(storageService.loadByPath(String.class, List.of("gameEngine"), "currentLanguage"))
|
||||
.thenReturn(Optional.of("de"));
|
||||
|
||||
// Load localizations with "de"
|
||||
Localizations localizationsToSave = new Localizations();
|
||||
Language german = new Language("German", "de");
|
||||
Map<String, String> germanEntries = new LinkedHashMap<>(Map.of("greeting", "Hallo"));
|
||||
localizationsToSave.getLanguagesInfo().put("de", german);
|
||||
localizationsToSave.getLanguages().put("de", germanEntries);
|
||||
localizationsToSave.setPrefix("i18n.");
|
||||
localizationsToSave.setCurId(1);
|
||||
|
||||
byte[] serialized = de.extio.game_engine.util.ObjectSerialization.serialize(
|
||||
localizationsToSave, false, false, false, null, digest -> {});
|
||||
InputStream stream = new ByteArrayInputStream(serialized);
|
||||
|
||||
this.localizationManager.load(stream);
|
||||
final LocalizationMetadata metadata = new LocalizationMetadata();
|
||||
metadata.getLanguagesInfo().put("de", new Language("German", "de"));
|
||||
metadata.setPrefix("i18n.");
|
||||
this.localizationManager.load(metadata, List.of(language("de", Map.of("greeting", "Hallo"))));
|
||||
|
||||
assertEquals("de", this.localizationManager.getCurrentLanguage());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user