aidiomix
v2.2.32
Published
AI powered translation tool
Downloads
3,253
Maintainers
Readme
Documentation for the aidiomix library
Introduction
aidiomix is an AI-driven translation tool designed to facilitate the management and generation of translations for your projects. It offers integration with multiple AI services to automate the translation of textual content found in your source files.
Before starting
aidiomix suppose that you already have installed a translation library like i18next or react-i18n.
When you are coding a new page or component, instead of having to choose a translation key that fits the string you want to translate, and go manualy update all your translation files, you just have to write the string in your component in the language you want,
...and let aidiomix do the work.
Example:
const MyComponent = () => {
return <div>Bonjour tout le monde</div>;
};aidiomix will choose a key for you and update your translation files and your code.
const MyComponent = () => {
return <div>{t('hello_world')}</div>;
};In this case, you need to add the useTranslation hook to your component to use the translations.
Installation
Install aidiomix using npm:
npm install aidiomixConfiguration
Step 1: Create an account and project
- Go to https://aidiomix.io and create an account
- Create a new project in the dashboard
- Copy your API key from the project settings
Step 2: Initialize configuration
Run the init command to create the configuration file:
npx aidiomix initThis will create a aidiomix.config.cjs file in the root of your project.
Step 3: Configure your project
Here's a complete example configuration:
module.exports = {
// Required: Languages to translate to
languages: ['en', 'fr', 'es', 'de', 'it'],
// Required: Path pattern for locale files (use {lang} as placeholder)
localesPath: './locales/{lang}/translation.json',
// Required: Directories to scan for source files
sourceDirectories: ['./app', './components'],
// Required: Your Aidiomix API key (get it from the dashboard)
apiKey: 'YOUR_API_KEY',
// Optional: Namespace generation strategy (default: 'full-path')
namespaceStrategy: 'full-path', // 'full-path' | 'filename' | 'x-first-parent' | 'x-last-parent' | 'global' | 'content-hash'
// Optional: Namespace file organization strategy (default: 'single-file')
// 'single-file': All namespaces in one file per language (e.g., locales/en/translation.json)
// 'separate-files': One file per namespace per language (e.g., locales/en/translation.common.json, locales/en/translation.buttons.json)
namespaceFileStrategy: 'single-file', // 'single-file' | 'separate-files'
// Optional: Text detection configuration (recommended)
textDetection: {
// Translation format (determines defaults)
translationFormat: 'i18next', // 'i18next' | 'react-intl' | 'next-intl'
// Pattern for replacing detected JSX text (default: "{t('$key')}")
// $key will be replaced with the translation key
replacementPattern: "{t('$key')}", // Examples: "t('$key')", "{formatMessage({ id: '$key' })}"
// Functions to detect (defaults based on translationFormat)
translationFunctions: ['t', 'i18n.t'], // Optional: override defaults
// Rich text components to detect (defaults based on translationFormat)
richTextComponents: ['Trans'], // Optional: override defaults
// Enable detection of untranslated JSX text (default: true)
detectUntranslatedJSX: true,
// Custom detection rules for specific patterns
customRules: [
{
// Regex pattern to match
pattern: /STATUS_LABELS\[(\w+)\]\s*=\s*"([^"]+)"/g,
// Optional: Custom processor function
processor: (detectedString, match) => {
return {
stringToResolve: match[2], // The text to translate
variables: [] // Optional: list of variables
};
},
// Priority (higher = processed first, default: 0)
priority: 10
}
]
},
// Deprecated: Use textDetection.translationFormat instead
translationFormat: 'i18next',
// Deprecated: Use textDetection.replacementPattern instead
translationFunction: 't'
};Configuration Options
Required Options
languages(string[]): Array of language codes to translate to (e.g.,['en', 'fr', 'es'])localesPath(string): Path pattern for locale files. Use{lang}as placeholder (e.g.,'./locales/{lang}/translation.json')sourceDirectories(string[]): Array of directories to scan for source files (e.g.,['./app', './components'])apiKey(string): Your Aidiomix API key from the dashboard
Optional Options
namespaceStrategy(string): Namespace generation strategy (default:'full-path')- See Namespace Strategy section for details
namespaceFileStrategy(string): How to organize namespace files (default:'single-file')'single-file': All namespaces are stored in one file per language- Example:
locales/en/translation.jsoncontains all namespaces - Structure:
{ "namespace1": {...}, "namespace2": {...} }
- Example:
'separate-files': Each namespace gets its own file per language- Example:
locales/en/translation.common.json,locales/en/translation.buttons.json - Each file contains only the translations for that namespace
- Namespace dots (
.) are replaced with dashes (-) in filenames - Example: namespace
common.buttons→ filetranslation.common-buttons.json
- Example:
Text Detection Configuration (textDetection)
The textDetection object allows you to customize how aidiomix detects and processes translatable text:
translationFormat(string): Translation library format'i18next'(default): For i18next/react-i18next'react-intl': For react-intl'next-intl': For next-intl
replacementPattern(string): Pattern for replacing detected JSX text- Default:
"{t('$key')}" $keywill be replaced with the translation key- Examples:
"{t('$key')}"for i18next"{formatMessage({ id: '$key' })}"for react-intl"{t('$key')}"for next-intl
- Default:
translationFunctions(string[]): Functions to detect as translation calls- Defaults based on
translationFormat:- i18next:
['t', 'i18n.t', 'i18next.t'] - react-intl:
['formatMessage', 't', 'intl.formatMessage'] - next-intl:
['t', 'useTranslations().t']
- i18next:
- Defaults based on
richTextComponents(string[]): Rich text components to detect- Defaults based on
translationFormat:- i18next:
['Trans'] - react-intl:
['FormattedMessage', 'FormattedNumber', 'FormattedDate'] - next-intl:
[]
- i18next:
- Defaults based on
detectUntranslatedJSX(boolean): Enable detection of untranslated JSX text (default:true)customRules(TextDetectionRule[]): Custom detection rules for specific patterns- Each rule has:
pattern: RegExp or string pattern to matchprocessor(optional): Function to process the match and extract the text to translatepriority(optional): Priority number (higher = processed first)
- Each rule has:
Deprecated Options (for backward compatibility)
translationFormat: UsetextDetection.translationFormatinsteadtranslationFunction: UsetextDetection.replacementPatterninstead
Namespace Strategy
aidiomix automatically organizes translations using namespaces based on the file structure. This helps keep your translations organized and prevents key conflicts.
You can configure the namespace strategy in your aidiomix.config.cjs file using the namespaceStrategy option.
Configuration
Add the namespaceStrategy option to your configuration:
module.exports = {
languages: ['en', 'fr', 'es'],
localesPath: './locales/{lang}/translation.json',
sourceDirectories: ['./app', './components'],
apiKey: 'YOUR_API_KEY',
translationFunction: 't',
namespaceStrategy: 'full-path', // or 'filename', 'first-parent', 'last-parent', 'global', 'content-hash'
};Available Strategies
1. Full Path (default: 'full-path')
Uses the complete file path relative to sourceDirectories as the namespace.
Example:
- File:
./app/components/ui/Button.tsx - Namespace:
components.ui.Button - Translation key:
components.ui.Button.hello_world
Pros:
- Automatic organization by file structure
- No key conflicts between different files
- Easy to navigate and find translations
Cons:
- Namespace changes when files are moved
- Can create long namespaces for deeply nested files
2. Filename Only ('filename')
Uses only the filename (without path or extension) as the namespace.
Example:
- File:
./app/components/ui/Button.tsx - Namespace:
Button - Translation key:
Button.hello_world
Pros:
- Namespace never changes when moving files
- Simple and readable
Cons:
- Risk of key conflicts if multiple files have the same name
- Less organization for large projects
3. X First Parent Directories ('x-first-parent')
Uses the first X parent directories as the namespace. Replace x with the number of directories you want.
Examples:
'1-first-parent': File./app/components/ui/Button.tsx→ Namespace:components(1 parent, filename excluded)'2-first-parent': File./app/components/ui/Button.tsx→ Namespace:components.ui(2 parents, filename excluded)'3-first-parent': File./app/components/ui/Button.tsx→ Namespace:components.ui(only 2 parents available, filename excluded)
Pros:
- Flexible - choose exactly how many parent directories to include
- More stable than full path
- Good for organizing by top-level directories
- Can include filename by using a higher number
Cons:
- Still changes if moving between different parent directories
- Need to choose the right number for your project structure
4. X Last Parent Directories ('x-last-parent')
Uses the last X parent directories (including filename) as the namespace. Replace x with the number of directories you want.
Examples:
'1-last-parent': File./app/components/ui/Button.tsx→ Namespace:Button(1 element: filename only)'2-last-parent': File./app/components/ui/Button.tsx→ Namespace:ui.Button(2 elements: 1 parent + filename)'2-last-parent': File./app/[lang]/client/campaigns/[id]/page.tsx→ Namespace:campaigns.id(2 elements: 1 parent + filename, normalized fromcampaigns.[id])'3-last-parent': File./app/components/ui/Button.tsx→ Namespace:components.ui.Button(3 elements: 2 parents + filename)
Pros:
- Flexible - choose exactly how many parent directories to include
- Handles dynamic route segments well (e.g.,
[lang],[id]) - More stable than full path
- Ideal for Next.js App Router projects
- Always includes the filename as the last part
Cons:
- Still changes if moving between different parent directories
- Need to choose the right number for your project structure
Handling Dynamic Route Segments:
For files with dynamic route segments like [lang], [id], or [...slug], the brackets are automatically removed:
[lang]→lang[id]→id[...slug]→slug
5. Global Namespace ('global')
No namespace prefix - all translations are at the root level.
Example:
- File:
./app/components/Button.tsx - Namespace: `` (empty)
- Translation key:
hello_world(no namespace prefix)
JSON structure:
{
"hello_world": "Hello World",
"click_me": "Click me"
}Pros:
- Simple, no namespace management
- Shorter translation keys
Cons:
- High risk of key conflicts
- Harder to organize large projects
- Not recommended for projects with many files
6. Content-Based Hash ('content-hash')
Uses a hash of the file content to create a stable namespace.
Example:
- File:
./app/components/Button.tsx - Namespace:
a1b2c3d4(first 8 characters of MD5 hash) - Translation key:
a1b2c3d4.hello_world
Pros:
- Very stable - namespace never changes unless file content changes
- No conflicts between different files
Cons:
- Not human-readable
- Harder to understand and navigate
- Namespace changes if file content changes significantly
Explicit Namespace Comment
Regardless of the configured strategy, you can override the namespace by adding a comment at the top of your file:
Single-line comment:
// @namespace: common.buttons
// or
// @aidiomix-namespace: common.buttonsMulti-line comment:
/* @namespace: common.buttons */
/* or */
/* @aidiomix-namespace: common.buttons */Example:
// @namespace: shared.components
const Button = () => {
return <div>Hello World</div>;
};The explicit namespace takes precedence over the configured strategy. This is useful when:
- You want to group translations from multiple files under the same namespace
- You need a specific namespace that doesn't match the file structure
- You want to ensure stability regardless of file location
Per-Key Namespace Override
You can also specify a namespace for individual translation keys by combining directives on the same line:
Syntax:
// @aidiomix-translate @aidiomix-namespace: <namespace>
// or
// @translate @namespace: <namespace>Examples:
// String literals
const message = "Hello"; // @aidiomix-translate @aidiomix-namespace: common
const button = "Click me"; // @translate @namespace: buttons
// JSX text
<div>
{/* @aidiomix-translate @aidiomix-namespace: common */}
Hello World
</div>
// JSX attributes
<Input
placeholder="Search..." // @aidiomix-translate @aidiomix-namespace: forms
/>Behavior:
- The per-key namespace takes precedence over both the file-level namespace comment and the configured strategy
- If no per-key namespace is specified, the file-level namespace (or strategy) is used
- Multiple directives can be combined on the same line
- Useful when you want specific keys to be grouped differently from the rest of the file
Example with mixed namespaces:
// File-level namespace: components.Button (from strategy)
const Button = () => {
const title = "Welcome"; // Uses file namespace: components.Button.text_xxx
const commonText = "Hello"; // @aidiomix-translate @aidiomix-namespace: common
// Uses specific namespace: common.text_xxx
return (
<div>
<h1>{title}</h1>
<p>{commonText}</p>
</div>
);
};Choosing the Right Strategy
Use full-path (default) when:
- Files are rarely moved
- You want maximum organization and no conflicts
- You have a stable project structure
Use filename when:
- Files are frequently moved
- You have unique filenames across your project
- You want simple, readable namespaces
Use x-first-parent when:
- You organize code by top-level directories (e.g.,
components/,pages/,features/) - You want moderate stability with some organization
- You want to control exactly how many parent directories to include
- Example:
'1-first-parent'for top-level only,'2-first-parent'for two levels
Use x-last-parent when:
- You're using Next.js App Router with dynamic routes
- You want good organization with better stability than full-path
- You need to handle files with dynamic segments like
[id]or[lang] - You want to control exactly how many parent directories to include from the end
- Example:
'2-last-parent'is often a good default (includes parent + filename)
Use global when:
- You have a small project with few files
- You want the simplest possible setup
- You're okay managing potential key conflicts manually
Use content-hash when:
- You need maximum stability regardless of file location
- You don't need human-readable namespaces
- You're building a tool or library where namespace readability isn't important
Text Detection Features
Automatic Detection
aidiomix automatically detects translatable text in the following contexts:
JSX Text Content: Text directly inside JSX elements
<div>Hello World</div> // ✅ DetectedJSX Attributes: Common attributes that contain user-facing text
placeholder- Input placeholderstitle- Tooltips and titlesalt- Image alt textaria-label- Accessibility labelsaria-describedby- Accessibility descriptionslabel- Form labels
<Input placeholder="Search..." /> // ✅ Detected <img alt="User avatar" /> // ✅ Detected <button title="Click me"> // ✅ DetectedString Literals: String values in your code
const message = "Welcome"; // ✅ Detected
Manual Tagging with Comments
You can explicitly control which texts should be translated using inline comments:
Force Translation
Use comments to mark texts that might not be automatically detected:
Syntax options:
// use translateor// @translateor// @t/* use translate */or/* @translate */// @aidiomix-translateor/* @aidiomix-translate */
Examples:
// In attributes
<Input
value="ACTIVE" // @translate
placeholder="[email protected]" // @no-translate
/>
// In string literals
const statusLabels = {
ACTIVE: "Active", // @translate
INACTIVE: "Inactive", // @translate
};
// In JSX text
<div>
{/* @translate */}
Technical term that should be translated
</div>
// In expressions
{status === "ACTIVE" ? (
"Actif" // @translate
) : (
"Inactif" // @translate
)}Multiple Directives on Same Line
You can combine multiple directives on the same line, such as specifying a namespace for a specific key:
Syntax:
// @aidiomix-translate @aidiomix-namespace: <namespace>
// or
// @translate @namespace: <namespace>Examples:
// String literals with namespace
const message = "Hello"; // @aidiomix-translate @aidiomix-namespace: common
const button = "Click me"; // @translate @namespace: buttons
// JSX text with namespace
<div>
{/* @aidiomix-translate @aidiomix-namespace: common */}
Hello World
</div>
// JSX attributes with namespace
<Input
placeholder="Search..." // @aidiomix-translate @aidiomix-namespace: forms
/>This allows you to override the namespace for individual keys, giving you fine-grained control over how translations are organized.
Exclude from Translation
Use comments to prevent automatic detection of technical text:
Syntax options:
// @no-translateor// @skip-translationor// @notranslate/* @no-translate */
Examples:
<Input placeholder="https://exemple.com" // @no-translate
<Input placeholder="[email protected]" // @skip-translation
Detection Rules
aidiomix uses intelligent heuristics to distinguish translatable text from technical values:
Automatically excluded:
- URLs (
https://example.com) - Email addresses (standalone)
- Purely numeric values
- Technical attributes (
href,src,className,onClick, etc.)
Automatically included:
- Text with spaces and letters (natural language)
- Text with accented characters (likely French, Spanish, etc.)
- Text with sentence punctuation (
. ! ?) - Multiple words (more than 2 words)
- Attributes in the translatable list (see above)
Best Practices
Avoid Variables in Translation Functions
For optimal parser performance, avoid using variables directly in translation functions. The parser cannot detect translation keys when they are stored in variables.
❌ Bad - Don't do this:
const label = 'button.label';
return t(label); // Parser cannot detect this key✅ Good - Use string literals:
return t('button.label'); // Parser can detect this keyWhy this matters:
- The parser scans your code statically and looks for string literals in translation functions
- When you use variables, the parser cannot determine the actual translation key at parse time
- This prevents the parser from:
- Detecting unused translation keys
- Tracking which keys are used in your codebase
- Providing accurate translation management
Alternative approaches:
If you need dynamic keys, always pass them directly to the t() function. This ensures the clean command can properly detect and track all used keys:
// ❌ Bad - Parser and clean command cannot detect this
const statusKey = `status.${status}`;
return t(statusKey);
// ✅ Good - Pass template literal directly to t()
return t(`status.${status}`); // Parser can detect the pattern
// ✅ Better - Use explicit keys with direct function calls
const statusKeys = {
active: 'status.active',
inactive: 'status.inactive',
pending: 'status.pending'
};
return t(statusKeys[status]); // More explicit, but clean may miss some keys
// ✅ Best - Pass keys directly to t() function
return status === 'active'
? t('status.active')
: status === 'inactive'
? t('status.inactive')
: t('status.pending');Important: The clean command scans your codebase to find unused translation keys. It can only detect keys that are passed directly as string literals or template literals to the t() function. Using intermediate variables or object lookups may cause the clean command to incorrectly mark keys as unused.
Use Explicit Translation Keys
Always prefer explicit, readable translation keys over dynamic generation:
❌ Bad:
const getKey = (type: string) => `common.${type}.label`;
return t(getKey('button'));✅ Good:
return t('common.button.label');Organize Translations by Feature
Group related translations using namespaces to keep your translation files organized:
✅ Good:
// In a button component
return t('components.Button.click_me');
// In a form component
return t('components.Form.submit');Use Comments for Context
Add comments to provide context for translators:
// Submit button in the checkout form
const submitLabel = "Submit Order"; // @translateKeep Translation Keys Descriptive
Use descriptive keys that clearly indicate the context:
❌ Bad:
t('key1')
t('text')
t('msg')✅ Good:
t('checkout.submit_button')
t('user.profile.edit_button')
t('error.invalid_email')Usage
Parse and translate files
To generate the translation files, run the following command:
npx aidiomix parseThis will parse the files and extract the non-translated strings Then it will translate them into the supported languages Then it will use IA to choose appropriate keys for the translations Then it will update the translation files with the new translations Then it will replace the non-translated strings with the new keys
Parse a single file
To parse and translate a single file, use the parse-file command:
npx aidiomix parse-file <filepath>For example:
npx aidiomix parse-file src/components/Button.tsxThis command will:
- Parse the specified file and extract non-translated strings
- Translate them into all configured languages
- Generate appropriate translation keys using AI
- Update the translation files with the new translations
- Replace the non-translated strings in the file with the translation keys
This is useful when you want to process a specific file without parsing all files in your source directories.
Clean unused translation keys
To remove unused translation keys from your language files, run:
npx aidiomix cleanThis command will:
- Scan all source files in your
sourceDirectoriesto find all used translation keys - Compare them with the keys in your language files
- Remove any keys that are not used in your code
- Clean up empty namespace objects after key removal
This is useful for keeping your translation files clean and removing keys that are no longer needed after refactoring or removing features.
