
A VS Code extension that automatically extracts hard-coded user-facing strings from your Angular source code, generates organized i18n JSON files with support for multiple locales, and automatically replaces strings in your source code with translation keys.

Features
- 🔍 Automatic String Extraction - Scans JS/TS/HTML files for user-facing strings
- 🧭 Zero Path Configuration - Detects the Angular project from where you trigger the command (nearest
angular.json, up or down), reads the source folder from its sourceRoot, and derives every other path from it — monorepos like projects/app/src work with no settings
- 📁 Consolidated Locale Output - Generates one locale JSON per language in
<sourceRoot>/assets/i18n (for example en-US.json, pt-BR.json)
- 🔄 Source Code Transformation - Replaces strings with translation keys (
{{ 'KEY' | translate }} in HTML, this.translateService.instant('KEY') in TS)
- 🧩 Auto-Import TranslateModule - Automatically adds TranslateModule to component imports arrays
- 💉 Collision-Safe TranslateService Injection - Reuses any existing injection (
inject() or constructor, any name); otherwise injects private translateService = inject(TranslateService), picking a name that never collides with your class members — and repairs collisions left by older versions
- 🛡️ Defensive Extraction - Never touches Angular control-flow syntax (
@if/@else/@for braces), HTML comments, CSS selectors in DOM calls (querySelectorAll), or strings outside a class where this. cannot compile
- 🌍 Smart Language Handling - Generate files for specific locales (en-US) or main languages (en) with automatic fallback
- 🎯 Active Language Filtering - Generate only active languages from your configuration
- 🔑 Nested Key Structure - UPPERCASE keys with underscores in hierarchical JSON format
- ✨ Auto-fill Metadata - Automatically generates language names and flags
- 🔄 Translation Preservation - Existing translations are never overwritten
- ✂️ Prune + Auto-Revert - Remove selected keys from default locale JSON and sync deletion/revert across workspace
- 🧹 Workspace Controls - Exclude paths from scan and clear extension workspace state from commands/menu
- 🚫 Smart Filtering - Automatically excludes technical strings, imports, object keys, state object values
- 📦 Custom Loader Generator - Creates optimized Angular HttpClient loader with language fallback support
- 🎨 main.ts Auto-Wiring - Automatically configures TranslateModule in your Angular bootstrap
- 🎨 Language Selector Component - Generates ready-to-use UI component for language switching
- 🔧 Translation Command Integration - Optional automatic translation after extraction
Installation
From VS Code Marketplace
- Open VS Code
- Go to Extensions (Ctrl+Shift+X or Cmd+Shift+X)
- Search for "Angular Translation Extractor"
- Click Install
From VSIX File
- Download the
.vsix file
- Open VS Code
- Go to Extensions
- Click the "..." menu → "Install from VSIX..."
- Select the downloaded file
Manual Installation
- Clone this repository
- Run
npm install
- Run
npm run compile
- Press F5 to open a new VS Code window with the extension loaded
Requirements
Dependencies
- VS Code: 1.85.0 or higher
- Node.js: For language metadata generation and extension runtime
- Angular Project: This extension is designed for Angular applications using
@ngx-translate/core
Required NPM Packages (in your Angular project)
npm install @ngx-translate/core @ngx-translate/http-loader
These packages are required for the generated translation loader to work in your Angular application.
Getting Started
1. Create a Languages List File
Create the languages list at <sourceRoot>/app/core/json/language-code.json (e.g. src/app/core/json/language-code.json) with your target locales:
[
{
"rank": 1,
"code": "en-US",
"default": true,
"active": true
},
{
"rank": 2,
"code": "pt-BR",
"active": true
},
{
"rank": 3,
"code": "es-ES",
"active": false
}
]
Field Descriptions:
code (required) - Locale code (e.g., "en-US", "pt-BR")
default (optional) - Set to true to use as default/base source language
active (optional) - Set to true to generate JSON for this language
rank (optional) - Sort order for language selection UI
englishName, nativeName, flag (auto-generated) - Extension will fill these automatically
2. That's It — Paths Are Detected
There is nothing to configure for paths. When you run a command, the extension:
- Finds the Angular project that owns the file/folder you triggered from — the nearest
angular.json, searched up from the trigger and then down from the opened folder. No angular.json anywhere → the command stops with a clear error.
- Reads the source folder from that project's
sourceRoot (so projects/app/src monorepos work untouched).
- Derives every path from it: locale JSONs go to
<sourceRoot>/assets/i18n, the languages list is <sourceRoot>/app/core/json/language-code.json, and main.ts comes from the build target in angular.json.
Behaviour (not paths) can still be customized in VS Code Settings (Ctrl+, → "Angular Translation Extractor"): minimum string length, HTML attributes to extract, aggressive mode, auto-translate, ignore globs.
You can run the extraction from multiple places:
- Command Palette:
Ctrl+Shift+P (Windows/Linux) or Cmd+Shift+P (Mac)
- Type: "Angular: Extract translations"
- Explorer context menu (right-click a folder)
- Editor title menu (toolbar)
The extension will:
- Scan your source files
- Extract user-facing strings
- Generate locale JSON files
- Auto-fill language metadata
- Generate translation loader and language selector component
- Display progress in the "Angular Translation Extractor" output channel
4. Check the Results
Source file:
src/components/LoginForm.ts
Generated translations:
src/assets/i18n/
├── en-US.json
├── pt-BR.json
└── es-ES.json
Generated utilities:
src/translate/
├── tg-translate-loader.ts (Translation loader)
├── readme.md (Setup instructions)
├── language-selector.component.ts (Language selector component)
├── language-selector.component.html (Component template)
└── language-selector.component.css (Component styles)
6. Use Language Selector
Import the standalone component and use it in your templates. It supports both light and dark modes:
import { Component } from '@angular/core';
import { TgLanguageSelectorComponent } from './translate/tg-language-selector.component';
@Component(
{
selector:
'app-header',
standalone: true,
imports:
[
TgLanguageSelectorComponent,
],
template: `
<nav>
<!-- Default Light Mode -->
<tg-language-selector></tg-language-selector>
or
<!-- Light Mode -->
<tg-language-selector
[mode]="
'white'
"
></tg-language-selector>
<!-- Dark Mode -->
<tg-language-selector
[mode]="
'dark'
"
></tg-language-selector>
</nav>
`,
},
)
export class HeaderComponent {}
JavaScript/TypeScript
Input:
const message =
'Welcome to our app';
const title = `User Profile`;
Generated base locale (en-US.json):
{
"COMPONENTS": {
"LOGINFORM": {
"WELCOME_TO_OUR_APP": "Welcome to our app",
"USER_PROFILE": "User Profile"
}
}
}
HTML
Input:
<button
title="Click to submit"
>
Send
</button>
<img
alt="Company logo"
src="https://github.com/devremoto/angular-translation-extractor/raw/HEAD/logo.png"
/>
Generated keys:
{
"COMPONENTS": {
"FORM": {
"CLICK_TO_SUBMIT": "Click to submit",
"SEND": "Send",
"COMPANY_LOGO": "Company logo"
}
}
}
Configuration Settings
Paths are not settings. The Angular project root, source folder, locale output folder, languages list and main.ts are all detected from your angular.json (see Getting Started). The old rootPath, srcDir, outputRoot, languagesJsonPath and mainTsPath settings were removed — delete them from your settings.json if present; they are ignored.
Behaviour can be configured through VS Code settings (settings.json) or the Settings UI:
| Setting ID |
Description |
Default |
i18nExtractor.aggressiveMode |
Function-parameter extraction mode: low, moderate, high |
moderate |
i18nExtractor.aggressiveModeAllowCallRegex |
Regex allowlist for full function-call source (priority over aggressiveMode) |
[^alert\\s*\\(, ^confirm\\s*\\(, ^prompt\\s*\\(] |
i18nExtractor.aggressiveModeAllowContextRegex |
Regex allowlist for argument context (priority over aggressiveMode) |
[^window\\.alert\\(arg#1\\)$, ^window\\.confirm\\(arg#1\\)$, ^window\\.prompt\\(arg#1\\)$] |
i18nExtractor.minStringLength |
Ignore strings shorter than this |
2 |
i18nExtractor.ignoreGlobs |
Glob patterns to ignore |
["\*\*/*.test._", "\*\*/_.spec.\*", "**/node_modules/**", "**/dist/**", "**/build/**", "**/.next/**", "**/.angular/**", "**/app.html", "**/index.html", "**/assets/**", "**/environments/**", "**/.agent/**", "**/.vscode/**"] |
i18nExtractor.skipGlobs |
Additional glob patterns to skip |
[] |
i18nExtractor.htmlAttributeNames |
HTML attributes to extract |
["title", "alt", "placeholder", "aria-label", "aria-placeholder"] |
i18nExtractor.angularBootstrapStyle |
standaloneormodule |
standalone |
i18nExtractor.updateMode |
Source source updates:merge, overwrite, recreate |
merge |
i18nExtractor.autoTranslate |
Automatically translate keys |
true |
i18nExtractor.autoTranslateDefaultLanguage |
Translate source language (usually false) |
false |
i18nExtractor.googleTranslateDelay |
Delay between translation API calls (ms) |
500 |
i18nExtractor.useTranslateCommand |
Run custom command after extration |
false |
i18nExtractor.translateCommand |
Custom translation command |
npx-translate |
i18nExtractor.translateArgsTemplate |
Arguments for custom command |
["--input", "{baseFile}", "--outDir", "{outDir}", "--from", "{baseLocale}", "--to", "{targetLocale}"] |
Language Configuration File
The <sourceRoot>/app/core/json/language-code.json file defines your supported languages:
[
{
"rank": 1,
"code": "en-US",
"englishName": "English (United States)",
"nativeName": "English (United States)",
"flag": "https://flagcdn.com/w40/us.png",
"default": true,
"active": true
},
{
"rank": 2,
"code": "pt-BR",
"englishName": "Portuguese (Brazil)",
"nativeName": "Português (Brasil)",
"flag": "https://flagcdn.com/w40/br.png",
"default": false,
"active": true
},
{
"rank": 3,
"code": "pt-PT",
"englishName": "Portuguese (Portugal)",
"nativeName": "Português (Portugal)",
"flag": "https://flagcdn.com/w40/pt.png",
"default": false,
"active": false
},
{
"rank": 4,
"code": "es-ES",
"englishName": "Spanish (Spain)",
"nativeName": "Español (España)",
"flag": "https://flagcdn.com/w40/es.png",
"default": false,
"active": true
},
{
"rank": 5,
"code": "fr-FR",
"englishName": "French (France)",
"nativeName": "Français (France)",
"flag": "https://flagcdn.com/w40/fr.png",
"default": false,
"active": true
},
{
"rank": 6,
"code": "it-IT",
"englishName": "Italian (Italy)",
"nativeName": "Italiano (Italia)",
"flag": "https://flagcdn.com/w40/it.png",
"default": false,
"active": true
},
{
"rank": 7,
"code": "zh-CN",
"englishName": "Chinese (Simplified, China)",
"nativeName": "中文 (中国)",
"flag": "https://flagcdn.com/w40/cn.png",
"default": false,
"active": true
}
]
Note: If this file doesn't exist, the extension will automatically create it with a default set of languages. Properties like englishName, nativeName, and flag are auto-filled if missing.
Output Structure
The extension replicates your source folder structure for organized locale files:
Source structure:
src/
├── components/
│ ├── LoginForm.ts
│ └── Header.html
└── pages/
└── Home.js
Generated output:
src/assets/I18n/
├── components/
│ ├── LoginForm/
│ │ ├── en-US.json
│ │ ├── pt-BR.json
│ │ └── es-ES.json
│ └── Header/
│ ├── en-US.json
│ ├── pt-BR.json
│ └── es-ES.json
└── pages/
└── Home/
├── en-US.json
├── pt-BR.json
└── es-ES.json
Translation Loader
Every extraction run also generates a translation loader inside your source directory:
src/translate/tg-translate-loader.ts
src/translate/readme.md
This loader is configured to load language files from your output directory.
Automatic Language Fallback
The generated loader includes intelligent language fallback:
- Loads files named exactly as requested (e.g.,
en-US.json, pt-BR.json)
This allows you to generate fewer files while supporting multiple regional variants:
// User selects "en-US" in the app
translateService.use(
'en-US',
);
// Loader tries "en-US.json" first
// Falls back to "en.json" if not found
Quick usage:
import { HttpClient } from '@angular/common/http';
import { TranslateLoader } from '@ngx-translate/core';
import { TgTranslationLoader } from './translate/tg-translate-loader';
export function HttpLoaderFactory(
http: HttpClient,
): TranslateLoader {
// The path is derived from your Angular project's sourceRoot
return new TgTranslationLoader(
http,
'./assets/I18n/',
);
}
The loader path is derived automatically from your Angular project's sourceRoot (locales are served from ./assets/i18n/). If you need a custom path, you can modify the prefix parameter. The auto-generated translate/readme.md file includes the full configuration details.
Language Selector Component
Every extraction run also generates a ready-to-use Language Selector Component that provides a dropdown UI for switching languages:
Generated files:
src/translate/tg-language-selector.component.ts - Anguar standalone component
src/translate/tg-language-selector.component.html - Template with dropdown UI
src/translate/tg-language-selector.component.css - Professional styles with dark mode support
Features
- 🎨 Professional UI - Modern dropdown with flags, language names, and smooth animations
- 🌓 Dark Mode Support - Automatically adapts to user's color scheme preference
- 🔄 Auto-Configuration - Reads languages from your i18n-languages.json
- 💾 Persistent Selection - Saves user's language choice to localStorage
- 🌍 Language Metadata - Shows native names, English names, and flags (if configured)
- ✨ Standalone Component - No module imports needed, works with modern Angular
Usage Example
The component is generated as a standalone component with the selector tg-language-selector. You can import and use it directly:
// In your app component or any standalone component
import { Component } from '@angular/core';
import { TgLanguageSelectorComponent } from './translate/tg-language-selector.component';
@Component(
{
selector:
'app-root',
standalone: true,
imports:
[
TgLanguageSelectorComponent,
],
template: `
<header>
<h1>
My
App
</h1>
<tg-language-selector></tg-language-selector>
</header>
<router-outlet></router-outlet>
`,
},
)
export class AppComponent {}
Customization
The component uses CSS variables for easy customization:
/* In your global styles.css or component styles */
.language-selector {
--background: #ffffff;
--hover-background: #f5f5f5;
--border-color: #e0e0e0;
--text-color: [#333333](https://github.com/devremoto/angular-translation-extractor/issues/333333);
--selected-background: #e3f2fd;
--selected-text-color: #1976d2;
}
For best results, ensure your i18n-languages.json includes display metadata:
[
{
"code": "en-US",
"englishName": "English (United States)",
"nativeName": "English",
"flag": "🇺🇸",
"default": true,
"active": true
},
{
"code": "pt-BR",
"englishName": "Portuguese (Brazil)",
"nativeName": "Português",
"flag": "🇧🇷",
"active": true
}
]
The component will auto-generate basic labels if metadata is missing, but providing full details creates a better user experience.
Configuration Reference
Access settings via: File → Preferences → Settings (or Ctrl+,) → Search for "Angular Translation Extractor"
Paths are detected, not configured — project root from the nearest angular.json, source folder from its sourceRoot, locales at <sourceRoot>/assets/i18n, languages list at <sourceRoot>/app/core/json/language-code.json, main.ts from the build target.
Basic Settings
| Setting |
Default |
Description |
i18nExtractor.aggressiveMode |
"moderate" |
Function-parameter extraction mode: "low" blocks all, "moderate" allows multi-word, "high" allows all |
i18nExtractor.aggressiveModeAllowCallRegex |
[^alert\\s*\\(, ^confirm\\s*\\(, ^prompt\\s*\\(] |
Regex allowlist matched against full call source (example: alert('teste')). Matches have priority over aggressiveMode |
i18nExtractor.aggressiveModeAllowContextRegex |
[^window\\.alert\\(arg#1\\)$, ^window\\.confirm\\(arg#1\\)$, ^window\\.prompt\\(arg#1\\)$] |
Regex allowlist matched against function argument context (example: this.toastr.error(arg#1)). Matches have priority over aggressiveMode |
i18nExtractor.minStringLength |
2 |
Ignore strings shorter than this length |
i18nExtractor.updateMode |
"merge" |
Controls JSON file updates:"merge"(preserve translations, add new keys),"overwrite"(recreate non-default languages),"recreate"(recreate all including default) |
i18nExtractor.angularBootstrapStyle |
"standalone" |
How to wire TranslateModule in main.ts (standaloneormodule) |
i18nExtractor.updateMainTs |
true |
If true, update main.ts to wire the translation loader |
Language Generation Settings
| Setting |
Default |
Description |
Language Generation Examples:
- Generates:
en-US.json, en-GB.json, pt-BR.json, es-ES.json
Filtering Settings
| Setting |
Default |
Description |
i18nExtractor.ignoreGlobs |
["**/*.test.*", "**/*.spec.*", ...] |
Glob patterns to exclude from scanning |
i18nExtractor.skipGlobs |
[] |
Additional glob patterns to exclude from scanning (merged with ignoreGlobs) |
i18nExtractor.htmlAttributeNames |
["title", "alt", "placeholder", ...] |
HTML attribute names to extract values from |
Default ignored patterns:
**/*.test.* - Test files
**/*.spec.* - Spec files
**/node_modules/** - Dependencies
**/dist/**, **/build/** - Build outputs
**/.next/** - Next.js build cache
**/main.ts - Angular main bootstrap file (hardcoded exclusion from extraction)
Default HTML attributes:
title - Element tooltips
alt - Image alt text
placeholder - Input placeholders
aria-label - Accessibility labels
aria-placeholder - Accessibility placeholders
Update Mode Settings
The updateMode setting controls how JSON translation files and generated artifacts are updated:
| Mode |
Default Language |
Non-Default Languages |
Generated Files (loader, readme, etc.) |
| merge (default) |
Merges with existing, preserves manual translations |
Preserves translations, adds new keys as blank |
Only writes if file doesn't exist |
| overwrite |
Merges with existing, preserves manual translations |
Recreates with all keys as blank |
Overwrites existing files |
| recreate |
Recreates, loses manual edits |
Recreates with all keys as blank |
Overwrites existing files |
When to use each mode:
Configuration:
{
"i18nExtractor.updateMode": "merge" // or "overwrite" or "recreate"
}
Automatic Translation Services
The extension supports automatic translation using free public translation APIs - no API key required!
Built-in Services
| Service |
Quality |
Speed |
API Key |
Rate Limit |
Recommended |
| Google Translate (Default) |
⭐⭐⭐⭐⭐ Excellent |
Fast |
No |
Yes (managed) |
✅ Yes |
Features:
- Automatic translation runs after string extraction
- Translates to all configured target languages
- Preserves manually edited translations
- Can be disabled per run or globally in settings
- Works offline after initial translation
Configuration
| Setting |
Default |
Description |
i18nExtractor.autoTranslate |
true |
If true, automatically initiate translations after extracting base locale strings |
i18nExtractor.autoTranslateDefaultLanguage |
false |
If false, skip translating the default language (marked with "default": true). The default language is the translation source. |
i18nExtractor.googleTranslateDelay |
500 |
Delay between translation requests in ms (minimum 100) |
Disable Automatic Translation
Set autoTranslate to false:
{
"i18nExtractor.autoTranslate": false
}
Custom Translation Commands
You can also use a custom translation command/service:
| Setting |
Default |
Description |
i18nExtractor.useTranslateCommand |
false |
Enable automatic translation using a custom command |
i18nExtractor.translateCommand |
"npx-translate" |
Command to run for translation |
i18nExtractor.translateArgsTemplate |
[...] |
Arguments template with placeholders |
Placeholder variables for translation command:
{baseFile} - Path to base locale JSON file
{outDir} - Output directory for the file
{baseLocale} - Base locale code (e.g., "en-US")
{targetLocale} - Target locale code (e.g., "pt-BR")
Example configuration:
{
"i18nExtractor.useTranslateCommand": true,
"i18nExtractor.translateCommand": "npx",
"i18nExtractor.translateArgsTemplate": [
"translate-json",
"--input",
"{baseFile}",
"--from",
"{baseLocale}",
"--to",
"{targetLocale}",
"--output-dir",
"{outDir}"
]
}
Create scripts/translate-google.js:
const fs = require('fs');
const path = require('path');
const axios = require('axios');
const OUTPUT_ROOT =
'src/assets/I18n';
const BASE_LANG =
'en';
async function translateText(
text,
targetLang,
) {
try {
const url = `https://translate.google.com/translate_a/single?client=gtx&sl=auto&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
const response =
await axios.get(
url,
{
headers:
{
'User-Agent':
'Mozilla/5.0',
},
},
);
return (
response.data[0]
?.map(
(
item,
) =>
item[0],
)
.join(
'',
) ||
text
);
} catch (err) {
console.error(
`Translation failed for "${text}": ${err.message}`,
);
return text;
}
}
async function translateFile(
inputFile,
outputDir,
targetLang,
) {
console.log(
`Translating to ${targetLang}...`,
);
const data =
JSON.parse(
fs.readFileSync(
inputFile,
'utf-8',
),
);
async function translateObj(
obj,
) {
for (const [
key,
value,
] of Object.entries(
obj,
)) {
if (
typeof value ===
'string' &&
value.trim()
.length >
0
) {
obj[
key
] =
await translateText(
value,
targetLang,
);
} else if (
typeof value ===
'object' &&
value !==
null &&
!Array.isArray(
value,
)
) {
await translateObj(
value,
);
}
}
}
await translateObj(
data,
);
const outputFile =
path.join(
outputDir,
`${targetLang}.json`,
);
fs.writeFileSync(
outputFile,
JSON.stringify(
data,
null,
2,
) +
'\n',
);
console.log(
` ✓ Saved: ${outputFile}`,
);
}
(async () => {
try {
const glob = require('glob');
const baseFiles =
glob.sync(
path.join(
OUTPUT_ROOT,
`**/${BASE_LANG}.json`,
),
);
console.log(
`Found ${baseFiles.length} base files\n`,
);
for (const baseFile of baseFiles) {
const outDir =
path.dirname(
baseFile,
);
const targetLangs =
[
'pt',
'es',
'fr',
]; // Customize as needed
for (const lang of targetLangs) {
await translateFile(
baseFile,
outDir,
lang,
);
await new Promise(
(
r,
) =>
setTimeout(
r,
500,
),
); // Rate limiting
}
}
console.log(
'\n✓ Translation completed!',
);
} catch (error) {
console.error(
'Error:',
error.message,
);
process.exit(
1,
);
}
})();
The languages JSON file should contain an array of locale entries. Only code is required; other fields will be auto-generated:
[
{
"rank": 1,
"code": "en-US",
"englishName": "English (United States)",
"nativeName": "English (United States)",
"flag": "https://flagcdn.com/w40/us.png",
"default": true,
"active": true
},
{
"rank": 2,
"code": "pt-BR",
"englishName": "Portuguese (Brazil)",
"nativeName": "português (Brasil)",
"flag": "https://flagcdn.com/w40/br.png",
"active": true
},
{
"rank": 3,
"code": "es-ES",
"englishName": "Spanish (Spain)",
"nativeName": "Español",
"flag": "https://flagcdn.com/w40/es.png",
"active": false
}
]
Field Descriptions:
| Field |
Required |
Type |
Auto-generated |
Description |
code |
✅ Yes |
string |
❌ No |
Locale code (e.g., "en-US", "pt-BR") |
rank |
❌ No |
number |
❌ No |
Sort order (lower ranks first) |
englishName |
❌ No |
string |
✅ Yes |
English display name |
nativeName |
❌ No |
string |
✅ Yes |
Native display name |
flag |
❌ No |
string |
✅ Yes |
Flag emoji or icon URL |
default |
❌ No |
boolean |
❌ No |
If true, use as default/base source language |
Features
After extraction, the extension automatically replaces extracted strings in your source files with translation keys:
HTML Files: Strings are replaced with Angular translate pipe syntax
<!-- Before -->
<button>
Submit
</button>
<p>
Welcome
to
our
app
</p>
<!-- After -->
<button>
{{
'COMPONENTS.FORM.SUBMIT'
|
translate
}}
</button>
<p>
{{
'COMPONENTS.FORM.WELCOME_TO_OUR_APP'
|
translate
}}
</p>
TypeScript/JavaScript Files: Strings are replaced with TranslateService calls
// Before
const message =
'Welcome to our app';
const title = `User Profile`;
// After
const message =
this.translateService.translate(
'COMPONENTS.PROFILE.WELCOME_TO_OUR_APP',
);
const title =
this.translateService.translate(
'COMPONENTS.PROFILE.USER_PROFILE',
);
Automatic TranslateModule Import: For components with HTML templates, the extension automatically:
- Adds
import { TranslateModule } from '@ngx-translate/core';
- Adds
TranslateModule to the component's imports array (for standalone components)
Automatic TranslateService Injection: For TypeScript files with inline string replacements:
- Adds
import { TranslateService } from '@ngx-translate/core';
- Injects
TranslateService into the component constructor
- Example:
constructor(private translateService: TranslateService) {}
Smart Key Generation
- Keys are automatically namespaced by file path in nested object structure
- Example:
COMPONENTS.LOGINFORM.WELCOME_MESSAGE
- Keys use UPPERCASE with underscores for word separation
- Keys are stable across multiple runs
- Collision handling with automatic numeric suffixes
Intelligent String Detection
The extension uses advanced AST analysis to extract:
- ✅ String literals in explicit display context:
"Hello World"
- ✅ Template literals (no expressions):
`Welcome`
- ✅ HTML text content:
<p>Content</p>
- ✅ HTML attribute values (configurable)
- ✅ Explicit alerts/messages:
alert("Message"), confirm("Sure?")
The extension ignores (Strict Filtering):
- ❌ Console logs:
console.log(...), console.error(...), console.warn(...)
- ❌ Decorators:
@Component selectors, @Injectable, @Pipe, @Directive metadata
- ❌ Logic: Strings in
if, for, while, switch statements
- ❌ Import/export paths:
import x from "./file"
- ❌ Object keys:
{ name: "value" }
- ❌ State object literals: Values in objects named
*State or assigned to *state variables
- ❌ URLs and paths:
"https://...", "/path/to/file"
- ❌ Tokens/IDs:
"ABC123XYZ"
- ❌ Template expressions:
"Hello ${name}"
- ❌ Placeholder syntax:
"{{variable}}", "${variable}"
- ❌ Pure numbers or punctuation
- ❌ Hex codes:
"3f4a5b"
State Object Filtering Example:
// ❌ These strings are NOT extracted (state configuration data)
const initialState: WizardState =
{
currentStep: 0,
templateExtension:
'pdf', // ← Not extracted
status:
'pending', // ← Not extracted
};
// ✅ These strings ARE extracted (user-facing text)
const message =
'Welcome to the wizard';
const label =
'Template Extension';
Deduplication
Identical strings within the same source file share the same key:
Input:
const btn1 =
'Submit';
const btn2 =
'Submit';
const btn3 =
'Cancel';
Output:
{
"COMPONENTS": {
"FORM": {
"SUBMIT": "Submit",
"CANCEL": "Cancel"
}
}
}
Translation Preservation
Existing translations are never overwritten:
Existing pt-BR.json:
{
"COMPONENTS": {
"FORM": {
"SUBMIT": "Enviar"
}
}
}
After re-running with new strings:
{
"COMPONENTS": {
"FORM": {
"SUBMIT": "Enviar",
"CANCEL": ""
}
}
}
New keys get empty values, existing translations are preserved.
The extension automatically completes language information using Node.js Intl API:
Your input:
[
{
"rank": 1,
"code": "pt-BR"
}
]
Auto-generated output:
[
{
"rank": 1,
"code": "pt-BR",
"englishName": "Portuguese (Brazil)",
"nativeName": "português (Brasil)",
"flag": "https://flagcdn.com/w40/br.png"
}
]
Usage Tips
1. Run Regularly
Run the extraction after adding new UI text to keep your locale files up to date.
2. Review Generated Keys
Check the base locale JSON files to ensure extracted strings are appropriate for translation.
3. Version Control
Commit both base and target locale files. Translators can work with the target locale files directly.
4. Start Small
Right-click a single folder and use Extract translations (File) / run on a subfolder first, then run Extract translations (All app) for the full source tree.
5. Customize Filtering
Add patterns to ignoreGlobs for any folders or files you don't want scanned.
Troubleshooting
- Check the Output panel (View → Output) and select "Angular Translation Extractor"
- Verify the command was triggered from inside your Angular project (the output panel logs the detected project root and source folder)
- Check if files are excluded by
ignoreGlobs patterns
- Increase
minStringLength if needed
- Adjust
minStringLength to filter out short strings
- Review
htmlAttributeNames if HTML extraction is too broad
- The heuristics aim to skip technical strings, but may not be perfect
- Ensure the languages JSON file exists at the configured path
- Check that
code field is present for each entry
- Verify the locale code format (e.g.,
"en-US", "pt-BR")
Validating Replacements
If replacements occur in the wrong location or fail silently:
- The extension validates that the text at the calculated position matches exactly before replacing.
- Check the "Angular Translation Extractor" output channel for warnings about mismatches.
- Ensure your files are saved with UTF-8 encoding.
How Locale Files Work
Each source file gets its own folder with separate JSON files per locale:
Base locale (en-US.json) - Contains extracted English strings:
{
"COMPONENTS": {
"HEADER": {
"WELCOME": "Welcome",
"LOGOUT": "Logout"
}
}
}
Target locales (pt-BR.json, es-ES.json) - Start with empty values:
{
"COMPONENTS": {
"HEADER": {
"WELCOME": "",
"LOGOUT": ""
}
}
}
Translators fill in the empty values, or use the optional translation command integration.
The extension uses a hierarchical nested object structure for better organization:
Structure
- Namespace: Based on the file path (e.g.,
COMPONENTS, PAGES, SERVICES)
- Component: Based on the filename (e.g.,
LOGINFORM, HEADER, SIDEBAR)
- Key: Based on the string content (e.g.,
WELCOME_MESSAGE, SUBMIT_BUTTON)
Naming Convention
- All keys are UPPERCASE
- Words are separated by underscores (
_)
- Special characters are removed or converted to underscores
- Keys are limited to 60 characters for readability
Example Key Path
File: src/components/LoginForm.ts
String: "Welcome to our app"
Generated Key Path: COMPONENTS.LOGINFORM.WELCOME_TO_OUR_APP
Collision Handling
If two different strings would generate the same key, the extension automatically appends a numeric suffix:
{
"COMPONENTS": {
"FORM": {
"TEXT": "First text",
"TEXT_2": "Another text"
}
}
}
Commands
| Command |
ID |
Description |
| Extract translations (All app) |
angularTranslation.extract |
Scan full source, generate/sync locale files, replace source strings |
| Extract translations (File) |
angularTranslation.extractFile |
Extract and replace strings for one file |
| Extract translations (Selection) |
angularTranslation.extractSelection |
Extract selected text and replace with translate.instant(...) / pipe |
| Extract Selection into ( 'KEY' | translate ) |
angularTranslation.extractSelectionParenthesis |
Extract selected expression into parenthesized pipe form |
| Reverse Translate (Folder) |
angularTranslation.reverseFromFolder |
Revert translated keys back to source strings in a folder |
| Reverse Translate (File) |
angularTranslation.reverseFromFile |
Revert translated keys in a single file |
| Reverse Translate (Selection) |
angularTranslation.reverseSelection |
Revert translated keys only in selected range |
| Prune Selected Translation Keys (JSON) |
angularTranslation.pruneSelectedKeys |
Remove selected keys from locale files and auto-revert usages |
| Exclude from Workspace Scan |
angularTranslation.excludeWorkspacePath |
Add selected file/folder to i18nExtractor.skipGlobs |
| Clear i18n Workspace State |
angularTranslation.clearWorkspaceState |
Clear extension workspace state with confirmation |
| Insert Language Selector |
angularTranslation.insertSelector |
Insert selector component tag in HTML and wire import |
Requirements
- VS Code 1.85.0 or higher
- Node.js (for language metadata generation)
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Author: Adilson de Almeida Pedro
Website: https://adilson.almeidapedro.com.br
GitHub: @devremoto
Twitter: @devremoto
LinkedIn: Adilson Pedro
📝 License
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 Support
If you encounter any issues or have questions:
- Check the Known Limitations section
- Search existing issues
- Create a new issue with detailed reproduction steps
⭐ Acknowledgments
- Thanks to the Angular team for creating an amazing framework
- Inspired by the need for better developer experience in Angular projects
- Built with love for the Angular community by Adilson de Almeida Pedro
💖 Support My Work
I build and maintain open-source projects to help the developer community.
If you find my work useful, please consider supporting future development! 🙌

Enjoy coding with Angular Translation Extractor! 🎉
Created by Adilson de Almeida Pedro - Full Stack Developer
Happy internationalizing! 🌍