Degoog — Translations

How translations work and how to add a new language.

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:

  1. DEGOOG_I18N environment variable, if set — forces a specific locale for every request.
  2. The browser's Accept-Language header — matched against available locale files (exact tag first, then language-only fallback, then en-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 corresponding en.json in each command's locales/ 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.