How it works
The server resolves the locale on every request, collects all
translations for that locale from every extension, serialises them
into window.__DEGOOG_T__, and injects the result into
the page before it reaches the browser. There is no client-side
locale loading.
The locale is resolved in this order:
-
DEGOOG_I18Nenvironment variable, if set — forces a specific locale for every request. -
The browser's
Accept-Languageheader — matched against available locale files (exact tag first, then language-only fallback, thenen-US).
On the client, window.scopedT("namespace") returns a
t(key) function scoped to a namespace within
window.__DEGOOG_T__. The core app uses
scopedT("core"). Extensions get their own namespace
automatically.
In HTML output returned by plugins and built-in commands, strings are
translated server-side using the {{ t:key }} syntax:
return {
html: `<p>{{ t:plugin-myname.some-string }}</p>`
};
Variables can be interpolated by passing comma-separated names after
the key. They map to {{ varname }} template placeholders
in the rendered output so the client-side template engine can
substitute them later:
// locale file
{ "plugin-myname.found": "Found on: {sources_text}" }
// plugin output
`{{ t:plugin-myname.found, sources_text }}`
Locale file format
Each locale file is a plain JSON object. Keys are dot-separated and
nested into objects. Values are strings and may contain named
placeholders in {curly braces} — do not rename them, but
you may reorder them.
{
"plugin-weather": {
"codes": {
"0": "Cielo sereno",
"unknown": "Sconosciuto"
},
"usage": {
"needCityLine1": "Uso: <code>!weather <città></code>"
}
}
}
Locale codes follow
BCP 47. The core settings page and default theme use the full tag (e.g.
en-US, fr-FR). Built-in commands and
third-party plugins use the short language code (e.g.
en, fr).
Core locale files
These are the files you need to create to add a new language to the core app. Each has an English original to base your translation on — only change the values, never the keys.
src/locales/<locale>.json-
Settings page — all tabs, modals, store UI, and errors. Copy from
src/locales/en-US.json. Uses full locale tags (en-US,fr-FR). -
src/public/themes/degoog-theme/locales/<locale>.json -
Home page and search results page — title, search button, tab
labels, footer links. Copy from
…/degoog-theme/locales/en-US.json. Uses full locale tags. -
src/server/extensions/commands/builtins/<command>/locales/<lang>.json -
Output strings for each built-in command. Commands with locale
files:
help,ip,ai-summary,speedtest,uuid. Copy from the correspondingen.jsonin each command'slocales/folder. Uses short language codes (en,fr).
Adding a new language — step by step
1. Copy the English originals
LANG=it-IT
SLANG=it
cp src/locales/en-US.json src/locales/$LANG.json
cp src/public/themes/degoog-theme/locales/en-US.json \
src/public/themes/degoog-theme/locales/$LANG.json
for cmd in help ip ai-summary speedtest uuid; do
cp src/server/extensions/commands/builtins/$cmd/locales/en.json \
src/server/extensions/commands/builtins/$cmd/locales/$SLANG.json
done
2. Translate the values
Open each copied file and translate every string value. Keep all
keys and placeholders ({like_this}) exactly as-is.
HTML in values (e.g. <code> tags) should be kept
and adapted to your language's wording.
3. Verify
Start the dev server with your locale forced and check everything:
DEGOOG_I18N=it-IT npm run dev
Go through the home page, every settings tab, the Configure modal for
an engine and a plugin, and trigger each built-in command
(!help, !ip, !uuid, etc.).
4. Open a pull request
Commit all new locale files and open a PR against
develop. Title it
feat(i18n): add <Language> translations. See
Contributing for the PR checklist.
Translations in third-party extensions
Plugins, engines, and themes distributed via the Store are each
responsible for their own translations. The pattern is identical:
add a locales/ folder inside the extension with one JSON
file per language code. The app picks up the files automatically.
For a plugin named my-plugin, the locale file is
locales/fr.json and the top-level key is
plugin-my-plugin. The plugin's display name,
description, settings field labels, and any output strings can all
live under that key. If you want to translate someone else's
extension, open a PR in their repo rather than here.