Skip to content

Add a Dictionary

This guide explains how to add a new dictionary or translation module to ClueLens. All source modules live under components/dicts/.

Core concepts

Each dictionary module consists of some of the following files:

FilePurposeRequired
search.tsRuns the search and returns result dataYes
view.tsxReact component for rendering resultsYes
types.tsType definitions for structured resultsRequired for structured dictionaries
configView.tsxConfiguration UIRequired when configuration exists
settingBackup.tsBackup and restore handlingRequired when configuration participates in backup
icon.pngSource iconYes

Two module patterns

Simple translators

Use this for services that return translated text, such as Google Translate, Caiyun, or DeepLx.

  • search.ts returns string[]
  • view.tsx can reuse the shared CommonView component
  • Reference implementations: google/, caiyun/, deeplx/

Structured dictionaries

Use this for sources that return structured data, such as Bing Dictionary, Jisho, or Urban Dictionary.

  • search.ts returns an array of custom types
  • types.ts defines those types
  • view.tsx is a custom renderer
  • Reference implementations: bing/, jisho/, urban/, wiktionary/

Five steps

Step 1: Register DictID

Add a new enum value to components/dicts/types.ts:

ts
export enum DictID {
  // existing values
  YourDict = 'yourDict',
}

Step 2: Create the module folder

text
components/dicts/yourDict/
├── icon.png
├── search.ts
├── view.tsx
├── types.ts
├── configView.tsx
└── settingBackup.ts

Step 3: Implement search.ts

The module must export a search function that returns an array.

Simple translator example:

ts
import { SearchOptions } from '../types';

export async function search(
  text: string,
  options?: SearchOptions
): Promise<string[]> {
  const query = text.trim();
  if (!query) return [];

  const translatedText = await callYourAPI(query, options?.targetLang);
  return translatedText ? [translatedText] : [];
}

Structured dictionary example:

ts
import { YourDictResult } from './types';

export async function search(text: string): Promise<YourDictResult[]> {
  const query = text.trim();
  if (!query) return [];

  const results = await fetchResults(query);
  return results;
}

Key points

  • Always call trim() on user input
  • Return [] for empty input
  • Translator modules can use options?.targetLang
  • Keep network logic inside search.ts

Step 4: Implement the renderer

Simple translator using CommonView:

tsx
import TranslateView from '../shared/CommonView';

export function ResultsView({ data }: { data: unknown[] }) {
  return <TranslateView result={data as string[]} />;
}

Structured dictionary with a custom view:

tsx
import { YourDictResult } from './types';

export function ResultsView({ data }: { data: unknown[] }) {
  const results = data as YourDictResult[];
  return (
    <div>
      {results.map((item, i) => (
        <div key={i}>
          <h3>{item.title}</h3>
          <p>{item.definition}</p>
        </div>
      ))}
    </div>
  );
}

Important

view.tsx must export a named component called ResultsView. Do not use a default export.

Step 5: Register metadata

In components/dicts/index.tsx:

  1. Import the icon:
ts
import yourDictIcon from './yourDict/icon.png';
  1. Add an entry to dictMetaMap:
ts
[DictID.YourDict]: {
  id: DictID.YourDict,
  displayName: 'Your Dictionary',
  displayNameKey: 'dict.yourDict',
  icon: yourDictIcon,
  language: { type: 'all' },
  type: ModuleType.Dict,
},

Meaning of language:

TypeMeaningExample
{ type: 'all' }Supports every languageGoogle Translate
{ type: 'monolingual', languages: ['en'] }Supports specific languages onlyDictionaryAPI
{ type: 'pairs', pairs: [['en', 'zh'], ['zh', 'en']] }Supports language pairsBing Dictionary

Meaning of type:

TypeMeaningEffect
ModuleType.DictDictionaryDoes not receive targetLang
ModuleType.TranslatorTranslatorReceives targetLang automatically
ModuleType.OtherOther, such as AIDoes not receive targetLang

Search modules and result views are discovered automatically through file naming conventions with import.meta.glob, so no extra loader wiring is needed.

Add configuration options

If the module needs an API key or custom settings:

  1. Define the config type and defaults in yourDict/types.ts
  2. Add a storage entry in utils/storage.ts
  3. Create yourDict/configView.tsx
  4. If the config must participate in backup and restore, create yourDict/settingBackup.ts and export settingsBackupHandler

Add localization

If the module name or configuration copy needs translation, add entries to each locale file under i18n/locales/:

json
{
  "dict.yourDict": "Your Dictionary"
}

Good references

Use caseReference module
Simple translatorgoogle/
Translator with configurationcaiyun/, deeplx/
Structured dictionaryurban/, jisho/
Custom HTML viewbing/
Dictionary with settings UIwiktionary/
AI outputopenai/