# 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]]