import * as vscode from 'vscode';
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { Logger } from './logger';
/**
* Enhanced L10n manager for Gorev VS Code extension
* Provides robust localization with proper fallback mechanisms
*/
export class L10nManager {
private static instance: L10nManager;
private initialized = false;
private bundles: Map<string, Record<string, string>> = new Map();
private currentLocale = 'en';
// Private constructor prevents external instantiation (singleton pattern)
private constructor() {
// Intentionally empty - singleton initialization happens in getInstance()
}
public static getInstance(): L10nManager {
if (!L10nManager.instance) {
L10nManager.instance = new L10nManager();
}
return L10nManager.instance;
}
/**
* Initialize the L10n system
*/
public async initialize(context: vscode.ExtensionContext): Promise<void> {
Logger.debug('[GOREV-L10N] 2. L10n manager initializing at:', new Date().toISOString());
Logger.debug('[GOREV-L10N] 3. Extension path:', context.extensionPath);
if (this.initialized) {
Logger.debug('[GOREV-L10N] 4. Already initialized, skipping');
return;
}
// Get current locale
this.currentLocale = vscode.env.language || 'en';
Logger.debug('[GOREV-L10N] 5. Current locale:', this.currentLocale);
// Load bundles
await this.loadBundles(context);
this.initialized = true;
Logger.debug('[GOREV-L10N] 6. L10n initialization completed');
}
/**
* Load localization bundles
*/
private async loadBundles(context: vscode.ExtensionContext): Promise<void> {
const l10nPath = join(context.extensionPath, 'l10n');
Logger.debug('[GOREV-L10N] 8. L10n path:', l10nPath);
// Load English bundle (fallback)
const enBundlePath = join(l10nPath, 'bundle.l10n.json');
Logger.debug('[GOREV-L10N] 9. EN bundle exists:', existsSync(enBundlePath));
if (existsSync(enBundlePath)) {
try {
const content = readFileSync(enBundlePath, 'utf8');
const bundle = JSON.parse(content);
this.bundles.set('en', bundle);
Logger.debug('[GOREV-L10N] 10. EN bundle loaded with', Object.keys(bundle).length, 'keys');
} catch (error) {
Logger.debug('[GOREV-L10N] 10. Failed to load English bundle:', error instanceof Error ? error.message : String(error));
}
}
// Load Turkish bundle
const trBundlePath = join(l10nPath, 'bundle.l10n.tr.json');
Logger.debug('[GOREV-L10N] 11. TR bundle exists:', existsSync(trBundlePath));
if (existsSync(trBundlePath)) {
try {
const content = readFileSync(trBundlePath, 'utf8');
const bundle = JSON.parse(content);
this.bundles.set('tr', bundle);
Logger.debug('[GOREV-L10N] 12. TR bundle loaded with', Object.keys(bundle).length, 'keys');
} catch (error) {
Logger.debug('[GOREV-L10N] 12. Failed to load Turkish bundle:', error instanceof Error ? error.message : String(error));
}
}
}
/**
* Get bundle URI for a specific locale
*/
private getBundleUri(context: vscode.ExtensionContext, locale: string): vscode.Uri | null {
const l10nPath = join(context.extensionPath, 'l10n');
let bundlePath: string;
if (locale.startsWith('tr')) {
bundlePath = join(l10nPath, 'bundle.l10n.tr.json');
} else {
bundlePath = join(l10nPath, 'bundle.l10n.json');
}
return existsSync(bundlePath) ? vscode.Uri.file(bundlePath) : null;
}
/**
* Translate a key with arguments
*/
public t(key: string, ...args: (string | number | boolean | Record<string, unknown>)[]): string {
if (!this.initialized) {
Logger.debug('[GOREV-L10N] 13. Manager not initialized, returning key:', key);
return key;
}
Logger.debug('[GOREV-L10N] 14. Translating key:', key, 'with', args.length, 'args');
return this.manualLookup(key, args);
}
/**
* Manual bundle lookup with fallback
*/
private manualLookup(key: string, args: (string | number | boolean | Record<string, unknown>)[]): string {
const simpleLocale = this.getSimpleLocale(this.currentLocale);
Logger.debug('[GOREV-L10N] 15. Looking up key:', key, 'for locale:', simpleLocale);
// Simple fallback chain: current locale → English → key
const bundle = this.bundles.get(simpleLocale) || this.bundles.get('en');
if (!bundle) {
Logger.debug('[GOREV-L10N] 16. No bundle found, returning key:', key);
return key;
}
const translation = bundle[key] || key;
Logger.debug('[GOREV-L10N] 17. Translation result:', translation);
// Replace placeholders if needed
if (args.length > 0) {
return this.replacePlaceholders(translation, args);
}
return translation;
}
/**
* Replace placeholders in translation
*/
private replacePlaceholders(text: string, args: (string | number | boolean | Record<string, unknown>)[]): string {
return text.replace(/\{(\d+)\}/g, (match, index) => {
const argIndex = parseInt(index, 10);
const arg = args[argIndex];
if (arg !== undefined) {
if (typeof arg === 'object') {
// For object parameters, try to extract the first value
const values = Object.values(arg);
return values.length > 0 ? String(values[0]) : match;
}
return String(arg);
}
return match;
});
}
/**
* Get simple locale (e.g., 'tr' from 'tr-TR')
*/
private getSimpleLocale(locale: string): string {
return locale.split('-')[0].toLowerCase();
}
/**
* Get current locale
*/
public getCurrentLocale(): string {
return this.currentLocale;
}
/**
* Check if manager is initialized
*/
public isInitialized(): boolean {
return this.initialized;
}
}
// Convenience function for global use
let globalManager: L10nManager | null = null;
/**
* Initialize global L10n manager
*/
export async function initializeL10n(context: vscode.ExtensionContext): Promise<void> {
globalManager = L10nManager.getInstance();
await globalManager.initialize(context);
}
/**
* Translate a key using global manager
*/
export function t(key: string, ...args: (string | number | boolean | Record<string, unknown>)[]): string {
if (!globalManager) {
Logger.warn('L10n not initialized, returning key');
return key;
}
return globalManager.t(key, ...args);
}
/**
* Get current locale
*/
export function getCurrentLocale(): string {
return globalManager?.getCurrentLocale() || 'en';
}