Complete Translation System Implementation Plan

Comprehensive step-by-step guide for implementing Transloco + Smartcat translation system across the GoGrab Space monorepo

🌐 Complete Translation System Implementation Plan

πŸ“‹ Executive Summary

This document provides a complete, step-by-step implementation plan for adding multi-language support to the GoGrab Space monorepo using Transloco (translation library) + Smartcat (AI-powered translation management).

Timeline Estimate: 2-3 weeks for full implementation
Team Size: 1-2 developers + AI automation
Languages: English (primary) β†’ Spanish, Portuguese (expandable)


🎯 Why This Solution?

Decision Matrix

Criteria Transloco + Smartcat ngx-translate @angular/localize
Performance ⭐⭐⭐⭐⭐ (3-5KB) ⭐⭐⭐ (15-20KB) ⭐⭐⭐⭐⭐ (2KB)
Runtime Switching βœ… Yes βœ… Yes ❌ No
AI Automation ⭐⭐⭐⭐⭐ Perfect ⭐⭐⭐⭐ Good ⭐⭐⭐ Limited
Ease of Use ⭐⭐⭐⭐⭐ Excellent ⭐⭐⭐⭐ Good ⭐⭐⭐ Complex
Angular 20 Support βœ… Full ⚠️ Maintained βœ… Official
Tree-shakable βœ… Yes ❌ No βœ… Yes
Dynamic Content βœ… Perfect βœ… Good ⚠️ Limited

Winner: Transloco + Smartcat offers the best balance for small teams needing performance, ease of use, and AI automation.


πŸ—οΈ Architecture Overview

System Components

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   TRANSLATION SYSTEM                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”‚
β”‚  β”‚   Transloco  │◄────►│  Smartcat AI β”‚                    β”‚
β”‚  β”‚   (Runtime)  β”‚      β”‚  (Management)β”‚                    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚
β”‚         β”‚                     β”‚                             β”‚
β”‚         β–Ό                     β–Ό                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚  β”‚      Translation Files (JSON)        β”‚                   β”‚
β”‚  β”‚  β”œβ”€β”€ en.json (English - Primary)    β”‚                   β”‚
β”‚  β”‚  β”œβ”€β”€ es.json (Spanish)              β”‚                   β”‚
β”‚  β”‚  └── pt.json (Portuguese)           β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚  β”‚    Application Layer                β”‚                   β”‚
β”‚  β”‚  β”œβ”€β”€ Static UI (Transloco)          β”‚                   β”‚
β”‚  β”‚  β”œβ”€β”€ Dynamic Content (MongoDB)       β”‚                   β”‚
β”‚  β”‚  └── User-Generated (Space Builder) β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Translation Layers

  1. Static UI Translations (Transloco)

    • Application interface (buttons, labels, menus)
    • Error messages, tooltips, notifications
    • Stored in: libs/libs-foundational/i18n/src/assets/i18n/{lang}.json
  2. Dynamic Platform Content (MongoDB + Transloco)

    • User-created universes, pages, components
    • Widget configurations, module descriptions
    • Stored in: Database with multi-language schema
  3. Documentation (Existing System)

    • Already implemented in docs-angular
    • Keep separate (working well)

πŸ“¦ Phase 1: Foundation Setup (Week 1, Days 1-3)

Step 1.1: Create Translation Library

Goal: Centralized NX library for all translation logic

# Create the i18n library
yarn create:lib:proper

# When prompted:
# Name: i18n
# Category: libs-foundational
# Type: Angular library
# Description: Internationalization system using Transloco

Expected Structure:

libs/libs-foundational/i18n/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ lib/
β”‚   β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”‚   β”œβ”€β”€ translation.service.ts        # Main service
β”‚   β”‚   β”‚   β”œβ”€β”€ transloco-loader.service.ts   # Custom loader
β”‚   β”‚   β”‚   └── language-detector.service.ts  # Auto-detect language
β”‚   β”‚   β”œβ”€β”€ models/
β”‚   β”‚   β”‚   β”œβ”€β”€ translation.models.ts         # TypeScript interfaces
β”‚   β”‚   β”‚   └── language.types.ts             # Language codes
β”‚   β”‚   β”œβ”€β”€ config/
β”‚   β”‚   β”‚   └── transloco.config.ts           # Transloco configuration
β”‚   β”‚   └── utils/
β”‚   β”‚       └── translation.utils.ts          # Helper functions
β”‚   β”œβ”€β”€ assets/
β”‚   β”‚   └── i18n/                             # Translation files
β”‚   β”‚       β”œβ”€β”€ en.json                       # English (primary)
β”‚   β”‚       β”œβ”€β”€ es.json                       # Spanish
β”‚   β”‚       └── pt.json                       # Portuguese
β”‚   └── index.ts                              # Public API
β”œβ”€β”€ project.json
β”œβ”€β”€ tsconfig.lib.json
└── README.md

Files to Create:

libs/libs-foundational/i18n/src/lib/models/translation.models.ts

/**
 * Supported language codes
 */
export type LanguageCode = 'en' | 'es' | 'pt';

/**
 * Language configuration
 */
export interface LanguageConfig {
  code: LanguageCode;
  label: string;
  nativeLabel: string;
  flag: string;
  direction: 'ltr' | 'rtl';
}

/**
 * Translation namespace for scoped translations
 */
export type TranslationNamespace = 
  | 'common'        // Shared translations
  | 'auth'          // Authentication
  | 'dashboard'     // Dashboard
  | 'editor'        // Editor
  | 'space-map'     // Space Map
  | 'singularity'   // Singularity
  | 'widgets'       // Widgets
  | 'errors'        // Error messages
  | 'validation';   // Form validation

/**
 * Dynamic content translation structure
 */
export interface DynamicTranslation {
  [key: string]: {
    [lang in LanguageCode]?: string;
  };
}

libs/libs-foundational/i18n/src/lib/config/transloco.config.ts

import { 
  provideTransloco, 
  TranslocoModule,
  TranslocoConfig,
  TRANSLOCO_SCOPE,
  TRANSLOCO_MISSING_HANDLER
} from '@ngneat/transloco';
import { isDevMode } from '@angular/core';
import { TranslocoHttpLoader } from '../services/transloco-loader.service';
import { LanguageCode } from '../models/translation.models';

/**
 * Transloco configuration for GoGrab Space
 */
export const translocoConfig: TranslocoConfig = {
  availableLangs: [
    { id: 'en', label: 'English' },
    { id: 'es', label: 'EspaΓ±ol' },
    { id: 'pt', label: 'PortuguΓͺs' }
  ],
  defaultLang: 'en',
  fallbackLang: 'en',
  reRenderOnLangChange: true,
  prodMode: !isDevMode(),
  flatten: {
    aot: true // Flatten keys in production for better performance
  },
  missingHandler: {
    useFallbackTranslation: true,
    logMissingKey: isDevMode()
  }
};

/**
 * Provide Transloco configuration
 * Use this in app.config.ts or standalone components
 */
export function provideTranslation() {
  return [
    provideTransloco({
      config: translocoConfig,
      loader: TranslocoHttpLoader
    })
  ];
}

libs/libs-foundational/i18n/src/lib/services/transloco-loader.service.ts

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Translation, TranslocoLoader } from '@ngneat/transloco';
import { Observable } from 'rxjs';
import { LanguageCode } from '../models/translation.models';

/**
 * Custom Transloco loader for loading translation files
 * Supports both assets and remote loading
 */
@Injectable({ providedIn: 'root' })
export class TranslocoHttpLoader implements TranslocoLoader {
  private http = inject(HttpClient);

  /**
   * Load translation file for specified language
   */
  getTranslation(lang: LanguageCode): Observable<Translation> {
    // Load from assets folder
    return this.http.get<Translation>(`/assets/i18n/${lang}.json`);
    
    // Alternative: Load from backend
    // return this.http.get<Translation>(`/api/translations/${lang}`);
  }
}

libs/libs-foundational/i18n/src/lib/services/translation.service.ts

import { Injectable, inject, signal, computed } from '@angular/core';
import { TranslocoService } from '@ngneat/transloco';
import { Observable } from 'rxjs';
import { LanguageCode, LanguageConfig } from '../models/translation.models';

/**
 * Translation service wrapper for GoGrab Space
 * Provides simplified API and app-specific functionality
 */
@Injectable({ providedIn: 'root' })
export class TranslationService {
  private transloco = inject(TranslocoService);

  // Current language signal
  private currentLangSignal = signal<LanguageCode>('en');
  
  // Available languages
  readonly availableLanguages: LanguageConfig[] = [
    {
      code: 'en',
      label: 'English',
      nativeLabel: 'English',
      flag: 'πŸ‡ΊπŸ‡Έ',
      direction: 'ltr'
    },
    {
      code: 'es',
      label: 'Spanish',
      nativeLabel: 'EspaΓ±ol',
      flag: 'πŸ‡ͺπŸ‡Έ',
      direction: 'ltr'
    },
    {
      code: 'pt',
      label: 'Portuguese',
      nativeLabel: 'PortuguΓͺs',
      flag: 'πŸ‡΅πŸ‡Ή',
      direction: 'ltr'
    }
  ];

  // Current language configuration
  readonly currentLanguage = computed(() => 
    this.availableLanguages.find(
      lang => lang.code === this.currentLangSignal()
    ) || this.availableLanguages[0]
  );

  constructor() {
    // Subscribe to language changes
    this.transloco.langChanges$.subscribe(lang => {
      this.currentLangSignal.set(lang as LanguageCode);
      this.persistLanguagePreference(lang as LanguageCode);
    });

    // Load saved language preference
    this.loadLanguagePreference();
  }

  /**
   * Get translation for a key
   */
  translate(key: string, params?: Record<string, unknown>): string {
    return this.transloco.translate(key, params);
  }

  /**
   * Get translation as observable (reactive)
   */
  translate$(key: string, params?: Record<string, unknown>): Observable<string> {
    return this.transloco.selectTranslate(key, params);
  }

  /**
   * Change application language
   */
  setLanguage(lang: LanguageCode): void {
    this.transloco.setActiveLang(lang);
  }

  /**
   * Get current language code
   */
  getCurrentLanguage(): LanguageCode {
    return this.currentLangSignal();
  }

  /**
   * Check if language is available
   */
  isLanguageAvailable(lang: string): boolean {
    return this.availableLanguages.some(l => l.code === lang);
  }

  /**
   * Persist language preference to local storage
   */
  private persistLanguagePreference(lang: LanguageCode): void {
    localStorage.setItem('gg-space-language', lang);
  }

  /**
   * Load language preference from local storage
   */
  private loadLanguagePreference(): void {
    const savedLang = localStorage.getItem('gg-space-language') as LanguageCode;
    if (savedLang && this.isLanguageAvailable(savedLang)) {
      this.setLanguage(savedLang);
    } else {
      // Auto-detect browser language
      const browserLang = navigator.language.split('-')[0] as LanguageCode;
      this.setLanguage(
        this.isLanguageAvailable(browserLang) ? browserLang : 'en'
      );
    }
  }
}

libs/libs-foundational/i18n/src/index.ts

// Public API
export * from './lib/models/translation.models';
export * from './lib/services/translation.service';
export * from './lib/services/transloco-loader.service';
export * from './lib/config/transloco.config';

Step 1.2: Install Transloco

# Install Transloco packages
npm install @ngneat/transloco --save

# Install dev tools for key extraction
npm install @ngneat/transloco-keys-manager --save-dev

Step 1.3: Configure Translation Files

Create initial translation files with basic structure:

libs/libs-foundational/i18n/src/assets/i18n/en.json

{
  "common": {
    "welcome": "Welcome",
    "save": "Save",
    "cancel": "Cancel",
    "delete": "Delete",
    "edit": "Edit",
    "loading": "Loading...",
    "error": "An error occurred"
  },
  "auth": {
    "login": "Log In",
    "logout": "Log Out",
    "email": "Email",
    "password": "Password",
    "forgotPassword": "Forgot Password?"
  },
  "dashboard": {
    "title": "Dashboard",
    "welcome": "Welcome to GoGrab Space",
    "subtitle": "Your journey to building amazing digital experiences starts here"
  }
}

libs/libs-foundational/i18n/src/assets/i18n/es.json

{
  "common": {
    "welcome": "Bienvenido",
    "save": "Guardar",
    "cancel": "Cancelar",
    "delete": "Eliminar",
    "edit": "Editar",
    "loading": "Cargando...",
    "error": "OcurriΓ³ un error"
  },
  "auth": {
    "login": "Iniciar SesiΓ³n",
    "logout": "Cerrar SesiΓ³n",
    "email": "Correo ElectrΓ³nico",
    "password": "ContraseΓ±a",
    "forgotPassword": "ΒΏOlvidaste tu ContraseΓ±a?"
  },
  "dashboard": {
    "title": "Panel de Control",
    "welcome": "Bienvenido a GoGrab Space",
    "subtitle": "Tu viaje para construir experiencias digitales increΓ­bles comienza aquΓ­"
  }
}

libs/libs-foundational/i18n/src/assets/i18n/pt.json

{
  "common": {
    "welcome": "Bem-vindo",
    "save": "Salvar",
    "cancel": "Cancelar",
    "delete": "Excluir",
    "edit": "Editar",
    "loading": "Carregando...",
    "error": "Ocorreu um erro"
  },
  "auth": {
    "login": "Entrar",
    "logout": "Sair",
    "email": "E-mail",
    "password": "Senha",
    "forgotPassword": "Esqueceu a Senha?"
  },
  "dashboard": {
    "title": "Painel",
    "welcome": "Bem-vindo ao GoGrab Space",
    "subtitle": "Sua jornada para construir experiΓͺncias digitais incrΓ­veis comeΓ§a aqui"
  }
}

Step 1.4: Update Library project.json

Ensure translation assets are copied during build:

libs/libs-foundational/i18n/project.json

{
  "name": "i18n",
  "$schema": "../../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "libs/libs-foundational/i18n/src",
  "prefix": "gg",
  "tags": ["scope:libs-foundational", "type:library"],
  "projectType": "library",
  "targets": {
    "build": {
      "executor": "nx:run-commands",
      "outputs": ["{workspaceRoot}/dist/libs/libs-foundational/i18n"],
      "options": {
        "command": "node tools/scripts/build/build-angular-library.js libs-foundational/i18n"
      },
      "configurations": {
        "production": {
          "command": "node tools/scripts/build/build-angular-library.js libs-foundational/i18n"
        }
      },
      "defaultConfiguration": "production",
      "dependsOn": ["^build"],
      "cache": true,
      "parallelism": true
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
      "options": {
        "jestConfig": "libs/libs-foundational/i18n/jest.config.ts"
      }
    }
  }
}

Step 1.5: Build the Library

# Build the i18n library
nx build i18n --skip-nx-cache

# Verify output structure
ls -la dist/libs/libs-foundational/i18n/
# Should see: index.d.ts, index.js, assets/ folder

πŸ”Œ Phase 2: Integration (Week 1, Days 4-5)

Step 2.1: Update App Configuration

apps/gograb-space/src/app/app.config.ts

import { ApplicationConfig, provideZoneChangeDetection, isDevMode } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';

// Import translation configuration
import { provideTranslation } from '@libs-foundational/i18n';

import { gSpaceMainRoutes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(gSpaceMainRoutes),
    provideHttpClient(),
    provideAnimations(),
    
    // Add translation support
    provideTranslation(),
    
    // ... other providers
  ]
};

Step 2.2: Copy Translation Assets

Update Angular build configuration to copy translation files:

apps/gograb-space/project.json

{
  "targets": {
    "build": {
      "executor": "@angular-devkit/build-angular:application",
      "options": {
        "assets": [
          "apps/gograb-space/src/favicon.ico",
          "apps/gograb-space/src/assets",
          {
            "glob": "**/*",
            "input": "libs/libs-foundational/i18n/src/assets",
            "output": "assets"
          }
        ]
      }
    }
  }
}

Step 2.3: Add to tsconfig paths

Verify path mapping exists in tsconfig.base.json:

{
  "compilerOptions": {
    "paths": {
      "@libs-foundational/i18n": ["libs/libs-foundational/i18n/src/index.ts"]
    }
  }
}

Step 2.4: Update project.json dependencies

apps/gograb-space/project.json

Add i18n library as dependency:

{
  "implicitDependencies": ["i18n"]
}

🎨 Phase 3: UI Implementation (Week 2, Days 1-3)

Step 3.1: Create Language Switcher Component

apps/gograb-space/src/app/components/shared/language-switcher/language-switcher.component.ts

import { Component, inject, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslationService, LanguageConfig } from '@libs-foundational/i18n';

@Component({
  selector: 'gg-language-switcher',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="language-switcher">
      <button 
        class="current-language"
        (click)="toggleDropdown()"
        [attr.aria-label]="'Switch language'">
        <span class="flag">{{ currentLanguage().flag }}</span>
        <span class="label">{{ currentLanguage().nativeLabel }}</span>
        <i class="fas fa-chevron-down"></i>
      </button>

      @if (isOpen()) {
        <div class="language-dropdown">
          @for (lang of availableLanguages; track lang.code) {
            <button
              class="language-option"
              [class.active]="lang.code === currentLanguage().code"
              (click)="selectLanguage(lang.code)">
              <span class="flag">{{ lang.flag }}</span>
              <span class="label">{{ lang.nativeLabel }}</span>
            </button>
          }
        </div>
      }
    </div>
  `,
  styleUrl: './language-switcher.component.scss'
})
export class LanguageSwitcherComponent {
  private translationService = inject(TranslationService);

  isOpen = signal(false);
  currentLanguage = this.translationService.currentLanguage;
  availableLanguages = this.translationService.availableLanguages;

  toggleDropdown(): void {
    this.isOpen.update(v => !v);
  }

  selectLanguage(code: string): void {
    this.translationService.setLanguage(code as any);
    this.isOpen.set(false);
  }
}

apps/gograb-space/src/app/components/shared/language-switcher/language-switcher.component.scss

.language-switcher {
  position: relative;
  
  .current-language {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    background: var(--ux-surface);
    border: 1px solid var(--ux-border);
    border-radius: var(--ux-radius-md);
    cursor: pointer;
    transition: all 0.2s ease;

    &:hover {
      background: var(--ux-surface-hover);
      border-color: var(--ux-primary);
    }

    .flag {
      font-size: 1.25rem;
    }

    .label {
      font-size: 0.875rem;
      font-weight: 500;
      color: var(--ux-text-primary);
    }

    i {
      font-size: 0.75rem;
      color: var(--ux-text-secondary);
      transition: transform 0.2s ease;
    }
  }

  .language-dropdown {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    min-width: 180px;
    background: var(--ux-surface);
    border: 1px solid var(--ux-border);
    border-radius: var(--ux-radius-md);
    box-shadow: var(--ux-shadow-lg);
    z-index: 1000;
    overflow: hidden;

    .language-option {
      width: 100%;
      display: flex;
      align-items: center;
      gap: 0.75rem;
      padding: 0.75rem 1rem;
      background: transparent;
      border: none;
      cursor: pointer;
      transition: background 0.2s ease;

      &:hover {
        background: var(--ux-surface-hover);
      }

      &.active {
        background: var(--ux-primary-alpha-10);
        
        .label {
          color: var(--ux-primary);
          font-weight: 600;
        }
      }

      .flag {
        font-size: 1.25rem;
      }

      .label {
        font-size: 0.875rem;
        color: var(--ux-text-primary);
      }
    }
  }
}

Step 3.2: Add Language Switcher to Header

Update apps/gograb-space/src/app/components/shared/header/header.component.ts

import { LanguageSwitcherComponent } from '../language-switcher/language-switcher.component';

@Component({
  // ...
  imports: [
    // ... existing imports
    LanguageSwitcherComponent
  ]
})
export class HeaderComponent {
  // ... existing code
}

Update apps/gograb-space/src/app/components/shared/header/header.component.html

<header class="app-header">
  <!-- ... existing header content ... -->
  
  <div class="header-actions">
    <!-- Add language switcher -->
    <gg-language-switcher />
    
    <!-- ... other header actions ... -->
  </div>
</header>

Step 3.3: Update Existing Components

Example: Dashboard Component

Before:

<h1>Welcome to GoGrab Space</h1>
<p>Your journey to building amazing digital experiences starts here.</p>

After:

<h1 transloco="dashboard.welcome">Welcome to GoGrab Space</h1>
<p transloco="dashboard.subtitle">Your journey to building amazing digital experiences starts here.</p>

Explanation:

Step 3.4: Import TranslocoModule in Components

For components using translations:

import { TranslocoModule } from '@ngneat/transloco';

@Component({
  standalone: true,
  imports: [
    CommonModule,
    TranslocoModule  // Add this
  ]
})
export class MyComponent { }

πŸ€– Phase 4: AI Translation Setup (Week 2, Days 4-5)

Step 4.1: Sign Up for Smartcat

  1. Create Account: https://www.smartcat.com/
  2. Choose Plan:
    • Free tier: Good for initial setup (500k words/month)
    • Pro: $99/month for larger projects
  3. Generate API Key: Account Settings β†’ API β†’ Generate Key

Step 4.2: Configure Smartcat CLI

# Install Smartcat CLI
npm install -g smartcat-cli

# Login to Smartcat
smartcat login

# Enter your API credentials when prompted
# API Key: [your-key]
# Account ID: [your-account-id]

Step 4.3: Create Smartcat Project

# Create new project
smartcat project create \
  --name "GoGrab Space Translations" \
  --source-language "en" \
  --target-languages "es,pt"

# Note the project ID returned

Step 4.4: Create Translation Workflow Script

tools/scripts/translation/translate.js

#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

/**
 * Automated translation workflow using Smartcat
 */
class TranslationWorkflow {
  constructor() {
    this.sourceFile = 'libs/libs-foundational/i18n/src/assets/i18n/en.json';
    this.targetLanguages = ['es', 'pt'];
    this.smartcatProjectId = process.env.SMARTCAT_PROJECT_ID;
  }

  /**
   * Extract all translation keys from source files
   */
  extractKeys() {
    console.log('πŸ“ Extracting translation keys...');
    try {
      execSync('npx transloco-keys-manager extract', { stdio: 'inherit' });
      console.log('βœ… Keys extracted successfully');
    } catch (error) {
      console.error('❌ Failed to extract keys:', error.message);
      process.exit(1);
    }
  }

  /**
   * Upload source file to Smartcat
   */
  uploadToSmartcat() {
    console.log('⬆️  Uploading to Smartcat...');
    
    try {
      execSync(
        `smartcat file upload \
          --project-id ${this.smartcatProjectId} \
          --file ${this.sourceFile}`,
        { stdio: 'inherit' }
      );
      console.log('βœ… Uploaded to Smartcat');
    } catch (error) {
      console.error('❌ Failed to upload:', error.message);
      process.exit(1);
    }
  }

  /**
   * Trigger AI translation
   */
  triggerTranslation() {
    console.log('πŸ€– Triggering AI translation...');
    
    try {
      execSync(
        `smartcat translation start \
          --project-id ${this.smartcatProjectId} \
          --use-ai`,
        { stdio: 'inherit' }
      );
      console.log('βœ… AI translation started');
      console.log('⏳ This may take a few minutes...');
    } catch (error) {
      console.error('❌ Failed to start translation:', error.message);
      process.exit(1);
    }
  }

  /**
   * Download translated files
   */
  downloadTranslations() {
    console.log('⬇️  Downloading translations...');

    this.targetLanguages.forEach(lang => {
      const outputFile = this.sourceFile.replace('en.json', `${lang}.json`);
      
      try {
        execSync(
          `smartcat file download \
            --project-id ${this.smartcatProjectId} \
            --language ${lang} \
            --output ${outputFile}`,
          { stdio: 'inherit' }
        );
        console.log(`βœ… Downloaded ${lang}.json`);
      } catch (error) {
        console.error(`❌ Failed to download ${lang}:`, error.message);
      }
    });
  }

  /**
   * Run complete workflow
   */
  async run() {
    console.log('🌐 Starting Translation Workflow\n');

    // Step 1: Extract keys from code
    this.extractKeys();

    // Step 2: Upload to Smartcat
    this.uploadToSmartcat();

    // Step 3: Trigger AI translation
    this.triggerTranslation();

    // Step 4: Wait for translation (poll every 30 seconds)
    await this.waitForTranslation();

    // Step 5: Download results
    this.downloadTranslations();

    console.log('\n✨ Translation complete!');
    console.log('πŸ“ Files updated in: libs/libs-foundational/i18n/src/assets/i18n/');
  }

  /**
   * Wait for translation to complete
   */
  async waitForTranslation() {
    return new Promise((resolve) => {
      const checkStatus = () => {
        try {
          const result = execSync(
            `smartcat translation status --project-id ${this.smartcatProjectId}`,
            { encoding: 'utf-8' }
          );

          if (result.includes('completed')) {
            console.log('βœ… Translation completed');
            resolve();
          } else {
            console.log('⏳ Still translating... (checking again in 30s)');
            setTimeout(checkStatus, 30000);
          }
        } catch (error) {
          console.error('❌ Error checking status:', error.message);
          setTimeout(checkStatus, 30000);
        }
      };

      checkStatus();
    });
  }
}

// Run workflow
const workflow = new TranslationWorkflow();
workflow.run().catch(error => {
  console.error('❌ Workflow failed:', error);
  process.exit(1);
});

Step 4.5: Add NPM Scripts

Update package.json

{
  "scripts": {
    "translate:extract": "npx transloco-keys-manager extract",
    "translate:upload": "node tools/scripts/translation/translate.js upload",
    "translate:download": "node tools/scripts/translation/translate.js download",
    "translate:auto": "node tools/scripts/translation/translate.js"
  }
}

Step 4.6: Configure Transloco Keys Manager

Create transloco.config.js in workspace root

module.exports = {
  rootTranslationsPath: 'libs/libs-foundational/i18n/src/assets/i18n/',
  langs: ['en', 'es', 'pt'],
  keysManager: {
    input: [
      'apps/gograb-space/src/**/*.{ts,html}',
      'libs/**/*.{ts,html}'
    ],
    output: 'libs/libs-foundational/i18n/src/assets/i18n',
    fileFormat: 'json',
    marker: 'transloco',
    addMissingKeys: true,
    emitErrorOnExtraKeys: false,
    replace: false
  }
};

πŸ’Ύ Phase 5: Dynamic Content Translation (Week 3)

Step 5.1: Update Database Schema

Add translation support to MongoDB models:

apis/backend-api/src/modules/universes/schemas/universe.schema.ts

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

@Schema()
export class UniverseTranslation {
  @Prop({ required: true })
  language: string; // 'en', 'es', 'pt'

  @Prop({ required: true })
  title: string;

  @Prop()
  description?: string;

  @Prop()
  metadata?: Record<string, any>;
}

@Schema({ timestamps: true })
export class Universe extends Document {
  @Prop({ required: true })
  ownerId: string;

  // Default language content
  @Prop({ required: true })
  title: string;

  @Prop()
  description?: string;

  // Multi-language translations
  @Prop({ type: [UniverseTranslation], default: [] })
  translations: UniverseTranslation[];

  // Helper method to get translated content
  getTranslation(lang: string): Partial<Universe> {
    const translation = this.translations.find(t => t.language === lang);
    if (translation) {
      return {
        title: translation.title,
        description: translation.description,
        ...translation.metadata
      };
    }
    // Fallback to default
    return {
      title: this.title,
      description: this.description
    };
  }
}

export const UniverseSchema = SchemaFactory.createForClass(Universe);

// Add helper method to schema
UniverseSchema.methods.getTranslation = function(lang: string) {
  const translation = this.translations.find(t => t.language === lang);
  if (translation) {
    return {
      title: translation.title,
      description: translation.description,
      ...translation.metadata
    };
  }
  return {
    title: this.title,
    description: this.description
  };
};

Step 5.2: Create Translation Service

libs/libs-foundational/i18n/src/lib/services/dynamic-translation.service.ts

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, map } from 'rxjs';
import { TranslationService } from './translation.service';

/**
 * Service for translating dynamic user-generated content
 */
@Injectable({ providedIn: 'root' })
export class DynamicTranslationService {
  private http = inject(HttpClient);
  private translationService = inject(TranslationService);

  /**
   * Translate content using Smartcat API
   */
  translateContent(params: {
    text: string;
    targetLanguages: string[];
    sourceLanguage?: string;
  }): Observable<Record<string, string>> {
    const { text, targetLanguages, sourceLanguage = 'en' } = params;

    return this.http.post<any>('/api/translations/translate', {
      text,
      sourceLanguage,
      targetLanguages
    }).pipe(
      map(response => response.translations)
    );
  }

  /**
   * Get translated content for current language
   */
  getTranslatedContent<T extends { translations?: any[] }>(
    content: T,
    fields: string[]
  ): T {
    const currentLang = this.translationService.getCurrentLanguage();
    
    if (currentLang === 'en' || !content.translations) {
      return content;
    }

    const translation = content.translations.find(
      t => t.language === currentLang
    );

    if (!translation) {
      return content;
    }

    // Merge translation with original content
    const translated = { ...content };
    fields.forEach(field => {
      if (translation[field]) {
        translated[field] = translation[field];
      }
    });

    return translated;
  }
}

Step 5.3: Create Backend Translation Endpoint

apis/backend-api/src/modules/translations/translations.controller.ts

import { Controller, Post, Body, UseGuards } from '@nestjs/common';
import { TranslationsService } from './translations.service';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';

@Controller('translations')
@UseGuards(JwtAuthGuard)
export class TranslationsController {
  constructor(private translationsService: TranslationsService) {}

  @Post('translate')
  async translate(@Body() dto: {
    text: string;
    sourceLanguage: string;
    targetLanguages: string[];
  }) {
    return this.translationsService.translateText(dto);
  }
}

apis/backend-api/src/modules/translations/translations.service.ts

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import axios from 'axios';

@Injectable()
export class TranslationsService {
  private smartcatApiKey: string;
  private smartcatAccountId: string;

  constructor(private configService: ConfigService) {
    this.smartcatApiKey = this.configService.get('SMARTCAT_API_KEY');
    this.smartcatAccountId = this.configService.get('SMARTCAT_ACCOUNT_ID');
  }

  async translateText(params: {
    text: string;
    sourceLanguage: string;
    targetLanguages: string[];
  }): Promise<{ translations: Record<string, string> }> {
    const { text, sourceLanguage, targetLanguages } = params;

    // Call Smartcat API
    const translations: Record<string, string> = {};

    for (const targetLang of targetLanguages) {
      try {
        const response = await axios.post(
          'https://smartcat.com/api/integration/v1/translate',
          {
            text,
            sourceLanguage,
            targetLanguage: targetLang
          },
          {
            headers: {
              'Authorization': `Bearer ${this.smartcatApiKey}`,
              'Content-Type': 'application/json'
            }
          }
        );

        translations[targetLang] = response.data.translation;
      } catch (error) {
        console.error(`Failed to translate to ${targetLang}:`, error);
        translations[targetLang] = text; // Fallback to original
      }
    }

    return { translations };
  }
}

Step 5.4: Update Frontend Components

Example: Universe Creation Component

import { DynamicTranslationService } from '@libs-foundational/i18n';

@Component({...})
export class UniverseCreationComponent {
  private dynamicTranslation = inject(DynamicTranslationService);

  async createUniverse(data: { title: string; description: string }) {
    // Translate to all supported languages
    const translations = await firstValueFrom(
      this.dynamicTranslation.translateContent({
        text: JSON.stringify(data),
        targetLanguages: ['es', 'pt']
      })
    );

    // Save universe with translations
    await this.universeService.create({
      ...data,
      translations: [
        { language: 'es', ...JSON.parse(translations.es) },
        { language: 'pt', ...JSON.parse(translations.pt) }
      ]
    });
  }
}

πŸ§ͺ Phase 6: Testing & Validation (Week 3)

Step 6.1: Manual Testing Checklist

Step 6.2: Create Translation Test Script

tools/scripts/translation/test-translations.js

#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

/**
 * Validate translation files
 */
class TranslationValidator {
  constructor() {
    this.translationsPath = 'libs/libs-foundational/i18n/src/assets/i18n';
    this.languages = ['en', 'es', 'pt'];
  }

  /**
   * Load translation file
   */
  loadTranslation(lang) {
    const filePath = path.join(this.translationsPath, `${lang}.json`);
    return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
  }

  /**
   * Get all keys from object (nested)
   */
  getAllKeys(obj, prefix = '') {
    let keys = [];
    
    for (const [key, value] of Object.entries(obj)) {
      const fullKey = prefix ? `${prefix}.${key}` : key;
      
      if (typeof value === 'object' && value !== null) {
        keys = keys.concat(this.getAllKeys(value, fullKey));
      } else {
        keys.push(fullKey);
      }
    }
    
    return keys;
  }

  /**
   * Validate all translations
   */
  validate() {
    console.log('πŸ” Validating translations...\n');

    const enTranslation = this.loadTranslation('en');
    const enKeys = this.getAllKeys(enTranslation);

    console.log(`βœ… English (source): ${enKeys.length} keys`);

    let hasErrors = false;

    // Validate each target language
    this.languages.slice(1).forEach(lang => {
      const translation = this.loadTranslation(lang);
      const keys = this.getAllKeys(translation);

      console.log(`\nπŸ“‹ ${lang.toUpperCase()}:`);
      console.log(`   Total keys: ${keys.length}`);

      // Check for missing keys
      const missingKeys = enKeys.filter(key => !keys.includes(key));
      if (missingKeys.length > 0) {
        console.log(`   ⚠️  Missing ${missingKeys.length} keys:`);
        missingKeys.slice(0, 5).forEach(key => {
          console.log(`      - ${key}`);
        });
        if (missingKeys.length > 5) {
          console.log(`      ... and ${missingKeys.length - 5} more`);
        }
        hasErrors = true;
      } else {
        console.log(`   βœ… All keys present`);
      }

      // Check for extra keys
      const extraKeys = keys.filter(key => !enKeys.includes(key));
      if (extraKeys.length > 0) {
        console.log(`   ⚠️  Extra ${extraKeys.length} keys (not in English):`);
        extraKeys.slice(0, 5).forEach(key => {
          console.log(`      - ${key}`);
        });
        hasErrors = true;
      }
    });

    console.log('\n' + '='.repeat(50));
    if (hasErrors) {
      console.log('❌ Validation failed - please fix missing/extra keys');
      process.exit(1);
    } else {
      console.log('βœ… All translations are valid!');
    }
  }
}

// Run validation
const validator = new TranslationValidator();
validator.validate();

Step 6.3: Add to CI/CD

Update .github/workflows/ci.yml or similar:

- name: Validate Translations
  run: node tools/scripts/translation/test-translations.js

πŸ“š Phase 7: Documentation (Week 3)

Step 7.1: Create Translation Guide

docs/english/GoGrab-Space/translations/translation-guide.md

# Translation System Guide

## Overview
GoGrab Space uses Transloco for translations with AI-powered management via Smartcat.

## For Developers

### Adding New Translations

1. Add translation key to English file:
   \`\`\`json
   // libs/libs-foundational/i18n/src/assets/i18n/en.json
   {
     "myFeature": {
       "title": "My Feature",
       "description": "Feature description"
     }
   }
   \`\`\`

2. Use in HTML:
   \`\`\`html
   <h1 transloco="myFeature.title">My Feature</h1>
   \`\`\`

3. Use in TypeScript:
   \`\`\`typescript
   title = this.translationService.translate('myFeature.title');
   \`\`\`

4. Run AI translation:
   \`\`\`bash
   npm run translate:auto
   \`\`\`

### Best Practices

- βœ… Use descriptive key names: `dashboard.welcome` not `d.w`
- βœ… Group related translations: `auth.login`, `auth.logout`
- βœ… Provide meaningful default text in templates
- βœ… Test all languages before merging
- ❌ Don't hardcode text in components
- ❌ Don't duplicate keys across namespaces

## For Content Creators

### Creating Multi-Language Content

When creating universes or pages:
1. Create content in your preferred language
2. System automatically translates to other languages
3. Review translations in Universe Settings
4. Edit if needed

## Translation Workflow

1. Developer adds keys β†’ English JSON
2. Run `npm run translate:auto`
3. Smartcat AI translates
4. Download updated translations
5. Commit to repository
6. Deploy

## Supported Languages

- πŸ‡ΊπŸ‡Έ English (primary)
- πŸ‡ͺπŸ‡Έ Spanish
- πŸ‡΅πŸ‡Ή Portuguese

Want more languages? Contact admin.

Step 7.2: Update Main Documentation

Add link to translation guide in main navigation.


πŸš€ Phase 8: Deployment (Week 3)

Step 8.1: Environment Variables

Add to .env files:

# Smartcat Configuration
SMARTCAT_API_KEY=your_api_key_here
SMARTCAT_ACCOUNT_ID=your_account_id_here
SMARTCAT_PROJECT_ID=your_project_id_here

Step 8.2: Update Backend Configuration

apis/backend-api/src/config/configuration.ts

export default () => ({
  // ... existing config
  smartcat: {
    apiKey: process.env.SMARTCAT_API_KEY,
    accountId: process.env.SMARTCAT_ACCOUNT_ID,
    projectId: process.env.SMARTCAT_PROJECT_ID
  }
});

Step 8.3: Deploy Checklist


πŸ“Š Monitoring & Maintenance

Translation Coverage Script

tools/scripts/translation/coverage.js

#!/usr/bin/env node

const fs = require('fs');
const glob = require('glob');

/**
 * Check translation coverage in codebase
 */
class TranslationCoverage {
  constructor() {
    this.sourceFiles = glob.sync('apps/gograb-space/src/**/*.{ts,html}');
    this.translationKeys = this.loadTranslationKeys();
  }

  loadTranslationKeys() {
    const enFile = 'libs/libs-foundational/i18n/src/assets/i18n/en.json';
    const data = JSON.parse(fs.readFileSync(enFile, 'utf-8'));
    return this.flattenKeys(data);
  }

  flattenKeys(obj, prefix = '') {
    let keys = [];
    for (const [key, value] of Object.entries(obj)) {
      const fullKey = prefix ? `${prefix}.${key}` : key;
      if (typeof value === 'object') {
        keys = keys.concat(this.flattenKeys(value, fullKey));
      } else {
        keys.push(fullKey);
      }
    }
    return keys;
  }

  analyze() {
    let hardcodedTexts = [];

    this.sourceFiles.forEach(file => {
      const content = fs.readFileSync(file, 'utf-8');
      
      // Find potential hardcoded text (simple heuristic)
      const matches = content.match(/>([A-Z][a-z\s]{3,})</g);
      if (matches) {
        matches.forEach(match => {
          const text = match.slice(1, -1).trim();
          if (!this.translationKeys.some(k => k.includes(text.toLowerCase()))) {
            hardcodedTexts.push({ file, text });
          }
        });
      }
    });

    console.log('πŸ“Š Translation Coverage Report\n');
    console.log(`Total translation keys: ${this.translationKeys.length}`);
    console.log(`Source files scanned: ${this.sourceFiles.length}`);
    console.log(`Potential hardcoded text: ${hardcodedTexts.length}\n`);

    if (hardcodedTexts.length > 0) {
      console.log('⚠️  Found potential hardcoded text:\n');
      hardcodedTexts.slice(0, 10).forEach(({ file, text }) => {
        console.log(`   ${file}`);
        console.log(`   β†’ "${text}"\n`);
      });
    } else {
      console.log('βœ… No hardcoded text detected!');
    }
  }
}

const coverage = new TranslationCoverage();
coverage.analyze();

Monthly Maintenance Tasks

  1. Review Translation Quality

    • Check Smartcat dashboard
    • Review user feedback
    • Update translations if needed
  2. Update Keys

    • Run extraction: npm run translate:extract
    • Review new keys
    • Translate with AI: npm run translate:auto
  3. Monitor Usage

    • Check language distribution
    • Identify most-used languages
    • Consider adding new languages

πŸŽ“ Training Materials

For Developers

Quick Start Video Script (to be recorded):

  1. Introduction (1 min)

    • What is Transloco?
    • Why we use it
    • Overview of workflow
  2. Adding Translations (3 min)

    • Add key to English JSON
    • Use in HTML template
    • Use in TypeScript
    • Run AI translation
  3. Testing (2 min)

    • Switch languages
    • Verify translations
    • Fix issues

For Content Creators

User Guide (to be created):

  1. Creating multi-language content
  2. Reviewing AI translations
  3. Editing translations
  4. Publishing in multiple languages

πŸ”§ Troubleshooting

Common Issues

Issue: Translations not loading

Symptoms: UI shows translation keys instead of text

Solution:

# Check if translation files exist
ls libs/libs-foundational/i18n/src/assets/i18n/

# Rebuild library
nx build i18n --skip-nx-cache

# Clear cache
nx reset

# Restart dev server
nx serve gograb-space

Issue: Language switch not working

Symptoms: Selected language doesn't change UI

Solution:

  1. Check browser console for errors
  2. Verify TranslocoModule imported
  3. Check if translation files loaded
  4. Clear localStorage: localStorage.clear()

Issue: AI translation failed

Symptoms: Smartcat returns errors

Solution:

# Check API credentials
echo $SMARTCAT_API_KEY

# Verify project ID
echo $SMARTCAT_PROJECT_ID

# Check Smartcat dashboard
smartcat project list

# Manual upload if needed
smartcat file upload --project-id XXX --file libs/libs-foundational/i18n/src/assets/i18n/en.json

πŸ“ˆ Success Metrics

KPIs to Track

  1. Translation Coverage: % of UI translated
  2. AI Accuracy: Manual edits needed vs AI translations
  3. Language Usage: Distribution of user languages
  4. Load Time: Impact on initial bundle size
  5. Developer Time: Time saved vs manual translation

Target Goals


πŸŽ‰ Launch Checklist

Pre-Launch

Launch Day

Post-Launch (Week 1)


πŸ“ž Support & Resources

Documentation Links

Team Contacts

Smartcat Support


🚦 Next Steps

After completing this implementation:

  1. Expand Languages

    • Add French, German, etc.
    • Based on user demand
  2. SEO Optimization

    • Implement multi-language URLs
    • Add hreflang tags
    • Create language-specific sitemaps
  3. Advanced Features

    • Context-aware translations
    • Pluralization rules
    • Date/number formatting
  4. Performance Optimization

    • Lazy load translations per module
    • Implement translation caching
    • Optimize bundle splitting

πŸ“ Appendix

A. Complete File Structure

editor/
β”œβ”€β”€ libs/libs-foundational/i18n/
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ lib/
β”‚   β”‚   β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ translation.service.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ transloco-loader.service.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ language-detector.service.ts
β”‚   β”‚   β”‚   β”‚   └── dynamic-translation.service.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ models/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ translation.models.ts
β”‚   β”‚   β”‚   β”‚   └── language.types.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ config/
β”‚   β”‚   β”‚   β”‚   └── transloco.config.ts
β”‚   β”‚   β”‚   └── utils/
β”‚   β”‚   β”‚       └── translation.utils.ts
β”‚   β”‚   β”œβ”€β”€ assets/
β”‚   β”‚   β”‚   └── i18n/
β”‚   β”‚   β”‚       β”œβ”€β”€ en.json
β”‚   β”‚   β”‚       β”œβ”€β”€ es.json
β”‚   β”‚   β”‚       └── pt.json
β”‚   β”‚   └── index.ts
β”‚   β”œβ”€β”€ project.json
β”‚   β”œβ”€β”€ tsconfig.lib.json
β”‚   └── README.md
β”œβ”€β”€ apps/gograb-space/
β”‚   └── src/
β”‚       └── app/
β”‚           β”œβ”€β”€ components/
β”‚           β”‚   └── shared/
β”‚           β”‚       └── language-switcher/
β”‚           β”‚           β”œβ”€β”€ language-switcher.component.ts
β”‚           β”‚           └── language-switcher.component.scss
β”‚           └── app.config.ts (updated)
β”œβ”€β”€ apis/backend-api/
β”‚   └── src/
β”‚       └── modules/
β”‚           └── translations/
β”‚               β”œβ”€β”€ translations.controller.ts
β”‚               β”œβ”€β”€ translations.service.ts
β”‚               └── translations.module.ts
β”œβ”€β”€ tools/scripts/translation/
β”‚   β”œβ”€β”€ translate.js
β”‚   β”œβ”€β”€ test-translations.js
β”‚   └── coverage.js
β”œβ”€β”€ transloco.config.js
└── docs/english/GoGrab-Space/translations/
    └── translation-guide.md

B. NPM Scripts Summary

{
  "translate:extract": "Extract keys from code",
  "translate:upload": "Upload to Smartcat",
  "translate:download": "Download from Smartcat",
  "translate:auto": "Full automated workflow",
  "translate:test": "Validate translation files",
  "translate:coverage": "Check translation coverage"
}

C. Environment Variables

# Required
SMARTCAT_API_KEY=
SMARTCAT_ACCOUNT_ID=
SMARTCAT_PROJECT_ID=

# Optional
DEFAULT_LANGUAGE=en
SUPPORTED_LANGUAGES=en,es,pt
TRANSLATION_CACHE_TTL=3600

D. Dependencies

{
  "dependencies": {
    "@ngneat/transloco": "^6.0.0"
  },
  "devDependencies": {
    "@ngneat/transloco-keys-manager": "^3.0.0",
    "smartcat-cli": "^1.0.0"
  }
}

βœ… Final Notes

This implementation plan provides:

  1. Complete Step-by-Step Guide: Every detail from setup to deployment
  2. Production-Ready Code: All necessary services, components, and configurations
  3. AI Automation: Full Smartcat integration for automated translations
  4. Best Practices: Following Angular 20 and Transloco recommendations
  5. Testing & Validation: Scripts and checklists for quality assurance
  6. Documentation: Comprehensive guides for developers and users
  7. Troubleshooting: Common issues and solutions
  8. Monitoring: Tools to track coverage and quality

Estimated Timeline: 2-3 weeks with 1-2 developers
Estimated Cost: Smartcat Free tier initially, ~$100/month for pro features
Expected ROI: 80% time savings on translation, support for global users


Document Version: 1.0
Last Updated: November 20, 2025
Status: Ready for Implementation
Next Review: After Phase 1 completion


Questions or Issues?
Contact the GoGrab Space team or refer to the resources section above.