Languages (i18n)
AllGame Club is written in English and translated into German. Every visible string in the portal and in the games comes from a language file, so adding a language or a game never means editing PHP views or portal code.
How the language is chosen
In this order, the first match wins (App\Core\I18n::boot(), called once per request in index.php):
?lang=dein the URL. The choice is stored in theagc_langcookie for a year and, for logged-in members, inusers.locale.- The
X-Localeheader, which the game SDK sends with every API call. - The
agc_langcookie. - The member's saved preference (Account → Profile → Language).
- The host name:
i18n.hostsin the config maps a domain to a locale ('allgameclub.de' => 'de'); any other.dedomain also opens in German. - The browser's
Accept-Languageheader. i18n.default(en).
The language switcher (header, mobile menu, footer, auth pages) links to the same page with ?lang=. Once a locale has its own domain, set i18n.urls ('de' => 'https://allgameclub.de') and the switcher and the hreflang links use the domains instead.
Configuration (config/config.php):
$config['i18n']['locales'] = ['en', 'de'];
$config['i18n']['hosts'] = ['allgameclub.de' => 'de'];
// $config['i18n']['urls'] = ['en' => 'https://allgameclub.com', 'de' => 'https://allgameclub.de'];
Portal strings
lang/{locale}/{group}.php returns a nested array. Keys are addressed as group.key (nested: account.profile.title).
| Group | Covers |
|---|---|
common | buttons, pills, flash labels, relative times, date formats, month/day names |
nav | header, user menu, footer |
home, games, auth, account, community, membership, developers, errors | one group per area |
pages | studio pages, FAQ, contact, legal page titles |
content | team bios, timeline, values, jobs, press facts, FAQ entries (arrays, merged by index in App\Services\Team) |
validation | validator messages and field labels |
mail | subjects and bodies of transactional mails |
play | play-session messages |
gameserver | English sentence → translation for game server modules |
In views and controllers:
__('games.show.left_today', ['time' => '12:00']) // placeholders: {time}
__('common.members_online', ['count' => 3]) // "one|many" forms picked by count
lang() // 'de'
game_text($game, 'tagline') // the manifest's i18n block, English otherwise
category_label('board'), unit_label('points')
English is the source. A key missing in German falls back to English; a key missing everywhere renders as the key itself (and is logged when app.debug is on). Long pages are translated as whole view copies: app/Views/pages/privacy.de.php wins over privacy.php when the locale is de (View::make tries {template}.{locale}.php first).
Emails are rendered in the recipient's language: wrap the send call in I18n::withLocale($user['locale'] ?? null, fn() => Mailer::send(...)).
Game strings
Every game ships lang/en.json and lang/de.json next to its entry file:
{
"ui": { "title": "Nine Mills", "start_game": "Start game" },
"app": { "your_move": "Your move", "players": "{count} player|{count} players" }
}
- Markup:
<button data-i18n="ui.start_game">Start game</button>. The SDK replaces the text once the dictionary is loaded; the inline English stays as the fallback. Alsodata-i18n-html,data-i18n-placeholder,data-i18n-title,data-i18n-aria-label. - Code:
ag.t('app.your_move'),ag.t('app.players', { count: 3 }),AllGame.tr('key', 'Fallback text'),AllGame.i18n.get('game.steps')for arrays.ag.localeis the active locale. - Manifest: translated name, tagline, description and controls go into
game.json:
"i18n": { "de": { "name": "Vier gewinnt", "tagline": "…", "description": "…", "controls": ["…"] } }
Run php bin/console.php games:sync after changing the manifest; the portal reads the block through game_text().
- Server modules keep their English messages in code.
$fail('not_found', 'Match not found.')passes the sentence throughI18n::literal(), which looks it up inlang/{locale}/gameserver.php. Add new sentences there. - Fairground games (
games/_kirmes): the framework translates its own UI throughkirmes.*keys and the game's config (title, tagline, goal, steps, scoring, HUD labels) throughgame.*keys. The config is the English fallback, so a fairground game only needslang/de.json; copygames/_kirmes/lang/de.example.json.
The SDK loads lang/{locale}.json (and lang/en.json as fallback) before AllGame.init() resolves, sends X-Locale on every API call, and the play shell passes &lang= to the iframe.
Adding a language
- Create
lang/{xx}/by copyinglang/en/and translating (keep the keys; plural forms stayone|many). - Add
xxtoi18n.locales; optionally a host ini18n.hosts. - Add
lang/{xx}.jsonto each game and ani18n.{xx}block to eachgame.json. - Add
lang/{xx}/gameserver.phpfor the server messages. - Copy the legal pages:
app/Views/pages/{terms,privacy,imprint}.{xx}.php.
App\Core\I18n::name('xx') holds the display names of common locales.
Last updated October 12, 2026.