feat(i18n): integrate StorageService for language persistence in LocalizationServiceImpl
This commit is contained in:
@@ -15,6 +15,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
|
|
||||||
import de.extio.game_engine.resource.StaticResource;
|
import de.extio.game_engine.resource.StaticResource;
|
||||||
import de.extio.game_engine.resource.StaticResourceService;
|
import de.extio.game_engine.resource.StaticResourceService;
|
||||||
|
import de.extio.game_engine.storage.StorageService;
|
||||||
|
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@ConditionalOnProperty(name = "game-engine.i18n.enabled", havingValue = "true", matchIfMissing = true)
|
@ConditionalOnProperty(name = "game-engine.i18n.enabled", havingValue = "true", matchIfMissing = true)
|
||||||
@@ -24,8 +25,8 @@ public class LocalizationAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@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) {
|
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();
|
final var localizationManager = new LocalizationServiceImpl(storageService);
|
||||||
|
|
||||||
if (loadOnStart) {
|
if (loadOnStart) {
|
||||||
final var path = new File(resource).toPath();
|
final var path = new File(resource).toPath();
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ public class LocalizationEditor extends JFrame {
|
|||||||
* Create the frame.
|
* Create the frame.
|
||||||
*/
|
*/
|
||||||
public LocalizationEditor() {
|
public LocalizationEditor() {
|
||||||
this.localizationManagerImpl = new LocalizationServiceImpl();
|
this.localizationManagerImpl = new LocalizationServiceImpl(null);
|
||||||
|
|
||||||
this.setTitle("i18n Editor");
|
this.setTitle("i18n Editor");
|
||||||
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import de.extio.game_engine.storage.StorageService;
|
||||||
import de.extio.game_engine.util.ObjectSerialization;
|
import de.extio.game_engine.util.ObjectSerialization;
|
||||||
|
|
||||||
public class LocalizationServiceImpl implements LocalizationService {
|
public class LocalizationServiceImpl implements LocalizationService {
|
||||||
@@ -17,12 +19,21 @@ public class LocalizationServiceImpl implements LocalizationService {
|
|||||||
|
|
||||||
public final static String NOT_FOUND_PREFIX = "{i18n.";
|
public final static String NOT_FOUND_PREFIX = "{i18n.";
|
||||||
|
|
||||||
|
private static final List<String> LANGUAGE_STORAGE_PATH = List.of("gameEngine");
|
||||||
|
private static final String LANGUAGE_STORAGE_NAME = "currentLanguage";
|
||||||
|
|
||||||
|
private final StorageService storageService;
|
||||||
|
|
||||||
private Localizations localizations = new Localizations();
|
private Localizations localizations = new Localizations();
|
||||||
|
|
||||||
private Map<String, String> currentLanguage;
|
private Map<String, String> currentLanguage;
|
||||||
|
|
||||||
private String currentLanguageName;
|
private String currentLanguageName;
|
||||||
|
|
||||||
|
LocalizationServiceImpl(final StorageService storageService) {
|
||||||
|
this.storageService = storageService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetEntries() {
|
public void resetEntries() {
|
||||||
this.localizations.getLanguages().forEach((lang, mapping) -> mapping.clear());
|
this.localizations.getLanguages().forEach((lang, mapping) -> mapping.clear());
|
||||||
@@ -74,10 +85,12 @@ public class LocalizationServiceImpl implements LocalizationService {
|
|||||||
LOGGER.info("Loaded {} localizations for {} languages", localizations.getDescriptions().size(), localizations.getLanguages().size());
|
LOGGER.info("Loaded {} localizations for {} languages", localizations.getDescriptions().size(), localizations.getLanguages().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.loadPersistedLanguageName();
|
||||||
if (this.currentLanguageName == null) {
|
if (this.currentLanguageName == null) {
|
||||||
this.currentLanguageName = "en";
|
this.currentLanguageName = "en";
|
||||||
}
|
}
|
||||||
this.setLanguage(this.currentLanguageName);
|
this.setLanguage(this.currentLanguageName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -88,6 +101,7 @@ public class LocalizationServiceImpl implements LocalizationService {
|
|||||||
|
|
||||||
this.currentLanguageName = lang;
|
this.currentLanguageName = lang;
|
||||||
this.currentLanguage = this.localizations.getLanguages().get(lang);
|
this.currentLanguage = this.localizations.getLanguages().get(lang);
|
||||||
|
this.persistCurrentLanguageName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -169,4 +183,30 @@ public class LocalizationServiceImpl implements LocalizationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void persistCurrentLanguageName() {
|
||||||
|
if (this.storageService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.storageService.store(LANGUAGE_STORAGE_PATH, LANGUAGE_STORAGE_NAME, this.currentLanguageName);
|
||||||
|
}
|
||||||
|
catch (final Exception e) {
|
||||||
|
LOGGER.warn("Could not persist current language name", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPersistedLanguageName() {
|
||||||
|
if (this.storageService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final Optional<String> loaded = this.storageService.loadByPath(String.class, LANGUAGE_STORAGE_PATH, LANGUAGE_STORAGE_NAME);
|
||||||
|
if (loaded.isPresent()) {
|
||||||
|
this.currentLanguageName = loaded.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final Exception e) {
|
||||||
|
LOGGER.warn("Could not load persisted language name", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class G2DThemeManager implements ThemeManager {
|
|||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(G2DThemeManager.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(G2DThemeManager.class);
|
||||||
|
|
||||||
private static final List<String> THEME_STORAGE_PATH = List.of("gameEngine", "themes");
|
private static final List<String> THEME_STORAGE_PATH = List.of("gameEngine");
|
||||||
private static final String THEME_STORAGE_NAME = "lastTheme";
|
private static final String THEME_STORAGE_NAME = "lastTheme";
|
||||||
|
|
||||||
private G2DRenderer g2dRenderer;
|
private G2DRenderer g2dRenderer;
|
||||||
|
|||||||
@@ -10,17 +10,29 @@ import java.io.InputStream;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
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.storage.StorageService;
|
||||||
|
|
||||||
class LocalizationManagerTest {
|
class LocalizationManagerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private StorageService storageService;
|
||||||
|
|
||||||
private LocalizationService localizationManager;
|
private LocalizationService localizationManager;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
this.localizationManager = new LocalizationServiceImpl();
|
MockitoAnnotations.openMocks(this);
|
||||||
|
this.localizationManager = new LocalizationServiceImpl(storageService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -278,4 +290,39 @@ class LocalizationManagerTest {
|
|||||||
assertEquals("Hello", englishGreeting);
|
assertEquals("Hello", englishGreeting);
|
||||||
assertEquals("Hallo", germanGreeting);
|
assertEquals("Hallo", germanGreeting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPersistCurrentLanguage() {
|
||||||
|
Localizations localizations = this.localizationManager.getLocalizations();
|
||||||
|
Language english = new Language("English", "en");
|
||||||
|
localizations.getLanguagesInfo().put("en", english);
|
||||||
|
localizations.getLanguages().put("en", new LinkedHashMap<>(Map.of("greeting", "Hello")));
|
||||||
|
|
||||||
|
this.localizationManager.setLanguage("en");
|
||||||
|
|
||||||
|
verify(storageService).store(List.of("gameEngine"), "currentLanguage", "en");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLoadPersistedLanguage() {
|
||||||
|
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);
|
||||||
|
|
||||||
|
assertEquals("de", this.localizationManager.getCurrentLanguage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import de.extio.game_engine.renderer.g2d.G2DRenderer;
|
||||||
|
import de.extio.game_engine.renderer.g2d.G2DRendererControl;
|
||||||
|
import de.extio.game_engine.renderer.model.options.UiOptions;
|
||||||
import de.extio.game_engine.resource.StaticResource;
|
import de.extio.game_engine.resource.StaticResource;
|
||||||
import de.extio.game_engine.resource.StaticResourceService;
|
import de.extio.game_engine.resource.StaticResourceService;
|
||||||
import de.extio.game_engine.storage.StorageService;
|
import de.extio.game_engine.storage.StorageService;
|
||||||
@@ -42,7 +45,7 @@ public class G2DThemeManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void loadsLastUsedThemeFromStorageOnConstruction() {
|
void loadsLastUsedThemeFromStorage() {
|
||||||
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
||||||
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
@@ -50,7 +53,7 @@ public class G2DThemeManagerTest {
|
|||||||
final Theme defaultTheme = theme("Default");
|
final Theme defaultTheme = theme("Default");
|
||||||
final Theme persistedTheme = theme("Persisted");
|
final Theme persistedTheme = theme("Persisted");
|
||||||
|
|
||||||
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine")), eq("lastTheme")))
|
||||||
.thenReturn(Optional.of(persistedTheme));
|
.thenReturn(Optional.of(persistedTheme));
|
||||||
|
|
||||||
final var manager = new G2DThemeManager(
|
final var manager = new G2DThemeManager(
|
||||||
@@ -59,19 +62,21 @@ public class G2DThemeManagerTest {
|
|||||||
List.of(patternRenderer),
|
List.of(patternRenderer),
|
||||||
Map.of("defaultKey", defaultTheme),
|
Map.of("defaultKey", defaultTheme),
|
||||||
"defaultKey");
|
"defaultKey");
|
||||||
|
manager.setCurrentTheme((Theme) null);
|
||||||
|
|
||||||
assertEquals("Persisted", manager.getCurrentTheme().getName());
|
assertEquals("Persisted", manager.getCurrentTheme().getName());
|
||||||
assertTrue(manager.getAvailableThemeNames().contains("Persisted"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void setCurrentThemePersistsLastThemeObject() {
|
void setCurrentThemePersistsLastThemeObject() {
|
||||||
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
final StaticResourceService staticResourceService = org.mockito.Mockito.mock(StaticResourceService.class);
|
||||||
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine")), eq("lastTheme")))
|
||||||
|
|
||||||
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
|
||||||
.thenReturn(Optional.empty());
|
.thenReturn(Optional.empty());
|
||||||
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
final G2DRendererControl g2dRendererControl = org.mockito.Mockito.mock(G2DRendererControl.class);
|
||||||
|
when(g2dRendererControl.getUiOptions()).thenReturn(new UiOptions());
|
||||||
|
final G2DRenderer g2dRenderer = org.mockito.Mockito.mock(G2DRenderer.class);
|
||||||
|
|
||||||
final Theme defaultTheme = theme("Default");
|
final Theme defaultTheme = theme("Default");
|
||||||
final var manager = new G2DThemeManager(
|
final var manager = new G2DThemeManager(
|
||||||
@@ -80,11 +85,13 @@ public class G2DThemeManagerTest {
|
|||||||
List.of(patternRenderer),
|
List.of(patternRenderer),
|
||||||
Map.of("defaultKey", defaultTheme),
|
Map.of("defaultKey", defaultTheme),
|
||||||
"defaultKey");
|
"defaultKey");
|
||||||
|
manager.setG2dRendererControl(g2dRendererControl);
|
||||||
|
manager.setG2dRenderer(g2dRenderer);
|
||||||
|
|
||||||
final Theme selected = theme("Selected");
|
final Theme selected = theme("Selected");
|
||||||
manager.setCurrentTheme(selected);
|
manager.setCurrentTheme(selected);
|
||||||
|
|
||||||
verify(storageService).store(eq(List.of("gameEngine", "themes")), eq("lastTheme"), eq(selected));
|
verify(storageService).store(eq(List.of("gameEngine")), eq("lastTheme"), eq(selected));
|
||||||
assertTrue(manager.getAvailableThemeNames().contains("Selected"));
|
assertTrue(manager.getAvailableThemeNames().contains("Selected"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +101,7 @@ public class G2DThemeManagerTest {
|
|||||||
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine")), eq("lastTheme")))
|
||||||
.thenReturn(Optional.empty());
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
final Theme defaultTheme = theme("Default");
|
final Theme defaultTheme = theme("Default");
|
||||||
@@ -106,7 +113,7 @@ public class G2DThemeManagerTest {
|
|||||||
"defaultKey");
|
"defaultKey");
|
||||||
|
|
||||||
final Theme loaded = theme("FromStorage");
|
final Theme loaded = theme("FromStorage");
|
||||||
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("FromStorage")))
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine")), eq("FromStorage")))
|
||||||
.thenReturn(Optional.of(loaded));
|
.thenReturn(Optional.of(loaded));
|
||||||
|
|
||||||
final Optional<Theme> result = manager.loadThemeFromStorage("FromStorage");
|
final Optional<Theme> result = manager.loadThemeFromStorage("FromStorage");
|
||||||
@@ -121,7 +128,7 @@ public class G2DThemeManagerTest {
|
|||||||
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine")), eq("lastTheme")))
|
||||||
.thenReturn(Optional.empty());
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
final Theme defaultTheme = theme("Default");
|
final Theme defaultTheme = theme("Default");
|
||||||
@@ -149,7 +156,7 @@ public class G2DThemeManagerTest {
|
|||||||
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine")), eq("lastTheme")))
|
||||||
.thenReturn(Optional.empty());
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
final Theme defaultTheme = theme("Default");
|
final Theme defaultTheme = theme("Default");
|
||||||
@@ -163,7 +170,7 @@ public class G2DThemeManagerTest {
|
|||||||
final Theme themeToSave = theme("UserTheme");
|
final Theme themeToSave = theme("UserTheme");
|
||||||
manager.saveThemeToStorage(themeToSave);
|
manager.saveThemeToStorage(themeToSave);
|
||||||
|
|
||||||
verify(storageService).store(eq(List.of("gameEngine", "themes")), eq("UserTheme"), eq(themeToSave));
|
verify(storageService).store(eq(List.of("gameEngine")), eq("UserTheme"), eq(themeToSave));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -204,7 +211,7 @@ public class G2DThemeManagerTest {
|
|||||||
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
final StorageService storageService = org.mockito.Mockito.mock(StorageService.class);
|
||||||
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
final PatternRenderer patternRenderer = org.mockito.Mockito.mock(PatternRenderer.class);
|
||||||
|
|
||||||
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine", "themes")), eq("lastTheme")))
|
when(storageService.loadByPath(eq(Theme.class), eq(List.of("gameEngine")), eq("lastTheme")))
|
||||||
.thenReturn(Optional.empty());
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
final Theme defaultTheme = theme("Default");
|
final Theme defaultTheme = theme("Default");
|
||||||
|
|||||||
Reference in New Issue
Block a user