# LocaleSource > Somewhere translations can be read from — a folder, resources in a jar, a hosted manifest, or memory. `gg.lode.bookshelflocales.source.LocaleSource` --- ## Signature ```java public interface LocaleSource ``` --- ## Methods | Method | Return Type | Description | |--------|-------------|-------------| | `load(Consumer<String> problems)` | `Map<String, Map<String, String>>` | Every locale this source can offer, keyed by language code. | | `describe()` | `String` | Short human-readable name, used in load warnings. | | `isRemote()` | `boolean` | Whether loading goes over the network. Defaults to `false`. | ### Contract - **Never throw for a missing or malformed entry.** A broken locale should cost that locale, not the plugin's startup. Return what could be read and hand the rest to `problems`. - **Return `true` from `isRemote()` if loading hits the network.** That is what separates `reloadFromDisk()` from `reloadFromCloud()` — a disk reload skips remote sources so an admin's file edit does not wait on HTTP. --- ## Implementations | Class | Description | |---|---| | `FolderLocaleSource` | Every `*.json` in a folder, file name as the language code. The server owner's side of the system. | | `ResourceLocaleSource` | Locales bundled in the plugin jar. Codes are declared up front, because a jar cannot be listed reliably at runtime. | | [[RemoteLocaleSource]] | Locales fetched from a hosted manifest, with an optional offline cache. | | `MapLocaleSource` | Translations held in memory. Useful for generated keys and for tests. | --- ## Precedence A [[LocaleManager]] loads every source in the order it was added, and a later source wins **per key**. The usual order is: ``` bundled defaults < hosted manifest < server owner's folder ``` Per-key precedence is what lets an owner override a single line without copying an entire file. --- ## Usage ```java public final class DatabaseLocaleSource implements LocaleSource { @Override public Map<String, Map<String, String>> load(Consumer<String> problems) { try { return queryTranslations(); } catch (SQLException e) { problems.accept("Could not read translations: " + e.getMessage()); return Map.of(); } } @Override public String describe() { return "translations table"; } @Override public boolean isRemote() { return true; } } ``` ```java LocaleManager.builder() .source(new DatabaseLocaleSource()) .folder(dataFolder.resolve("locales")) .build(); ``` --- ## Related Pages - [[LocaleManager]] - [[LocaleService]] - [[RemoteLocaleSource]]