Skip to content

Localization Development ​

This guide explains how to work with localization in Nuxt Users development, including adding new translations, testing, and quality assurance.

Development Scripts ​

The module includes two automated checking scripts to maintain translation quality:

check:translations ​

Scans Vue files for hardcoded strings that should use the t() translation function.

bash
yarn check:translations

What it checks:

  • Template strings in Vue components
  • Detects user-facing text that isn't translated
  • Filters out technical strings (CSS classes, SVG paths, etc.)

Example output:

[Nuxt Users] 🔍 Scanning Vue files in: src/runtime/components

[Nuxt Users] 📄 Found 11 Vue file(s)

[Nuxt Users] ⚠️  Found 1 potential hardcoded string(s):

📁 src/runtime/components/NUsersResetPasswordForm.vue
   Line 11:9
   String: "You are resetting your password..."
   Context: <p>You are resetting your password...</p>

[Nuxt Users] 💡 Tip: Replace hardcoded strings with t() function

check:locale-consistency ​

Ensures Hungarian formal and informal translation overrides are kept in sync.

bash
yarn check:locale-consistency

What it checks:

  • All formal variant keys have informal equivalents
  • All informal variant keys have formal equivalents
  • Reports mismatches with statistics

Example output:

[Nuxt Users] 🔍 Checking Hungarian locale formal/informal consistency...

[Nuxt Users] 📊 Statistics:
   Base keys: 85
   Informal override keys: 20
   Formal override keys: 20

[Nuxt Users] ✅ All formal/informal translations are consistent!

Both scripts run automatically during the release process.

File Structure ​

src/runtime/
├── locales/
│   ├── index.ts        # Exports all locales
│   ├── en.ts           # English translations
│   └── hu.ts           # Hungarian translations (informal + formal)
├── utils/
│   └── locale.ts       # Translation utilities
└── composables/
    └── useNuxtUsersLocale.ts  # Composable for translations

Adding a New Language ​

1. Create Locale File ​

Create a new file in src/runtime/locales/:

typescript
// src/runtime/locales/de.ts
import type { LocaleMessages } from '../../types'

export const de: LocaleMessages = {
  common: {
    email: 'E-Mail',
    password: 'Passwort',
    name: 'Name',
    // ... all common keys
  },
  login: {
    title: 'Willkommen zurück',
    subtitle: 'Melden Sie sich an',
    // ... all login keys
  },
  // ... all other sections
}

2. Export from Index ​

Add your locale to src/runtime/locales/index.ts:

typescript
import { en } from './en'
import { hu, huFormal } from './hu'
import { de } from './de'  // Add this

export const defaultLocaleMessages: Record<string, LocaleMessages> = {
  en,
  hu,
  'hu-formal': huFormal,
  de  // Add this
}

3. Test Your Translation ​

Create an integration test:

typescript
// test/integration/locale-de.test.ts
import { describe, it, expect } from 'vitest'
import { getTranslation } from '../../src/runtime/utils/locale'

describe('German Locale', () => {
  it('should translate login strings', () => {
    expect(getTranslation('login.title', 'de')).toBe('Willkommen zurück')
    expect(getTranslation('login.submit', 'de')).toBe('Anmelden')
  })
  
  it('should have all required keys', () => {
    const keys = [
      'login.title',
      'login.subtitle',
      'register.title',
      'resetPassword.titleReset'
      // ... test key translations exist
    ]
    
    keys.forEach(key => {
      const translation = getTranslation(key, 'de')
      expect(translation).not.toBe(key)  // Should not return key itself
    })
  })
})

4. Run Quality Checks ​

bash
# Check for hardcoded strings
yarn check:translations

# Run tests
yarn test:unit test/integration/locale-de.test.ts

Adding Translation Keys ​

When adding new features that require translations:

1. Add to English (Base) ​

Always start with English in src/runtime/locales/en.ts:

typescript
export const en: LocaleMessages = {
  // ... existing keys
  myNewFeature: {
    title: 'My New Feature',
    description: 'This is a new feature',
    action: 'Do Something'
  }
}

2. Add to All Other Locales ​

Add equivalent keys to all supported locales:

typescript
// src/runtime/locales/hu.ts
const huBase: LocaleMessages = {
  // ... existing keys
  myNewFeature: {
    title: 'Új Funkció',
    description: 'Ez egy új funkció',
    action: 'Csinálj Valamit'
  }
}

3. Add Tests ​

Test the new translations:

typescript
it('should translate myNewFeature keys', () => {
  expect(getTranslation('myNewFeature.title', 'en')).toBe('My New Feature')
  expect(getTranslation('myNewFeature.title', 'hu')).toBe('Új Funkció')
})

4. Use in Components ​

vue
<script setup>
const { t } = useNuxtUsersLocale()
</script>

<template>
  <div>
    <h2>{{ t('myNewFeature.title') }}</h2>
    <p>{{ t('myNewFeature.description') }}</p>
    <button>{{ t('myNewFeature.action') }}</button>
  </div>
</template>

Hungarian Formal/Informal Variants ​

Hungarian has two politeness levels that must be maintained:

Base (Shared) ​

Common, non-contextual terms go in huBase:

typescript
const huBase: LocaleMessages = {
  common: {
    email: 'E-mail',      // Same in both variants
    password: 'Jelszó',   // Same in both variants
    name: 'Név'           // Same in both variants
  }
}

Informal Overrides ​

Personal, casual terms for the hu locale:

typescript
const huInformalOverrides: LocaleMessages = {
  login: {
    rememberMe: 'Emlékezz rám',           // "you" informal
    forgotPassword: 'Elfelejtetted...?'   // "you" informal
  }
}

Formal Overrides ​

Professional, polite terms for the hu-formal locale:

typescript
const huFormalOverrides: LocaleMessages = {
  login: {
    rememberMe: 'Maradjon bejelentkezve',  // "you" formal
    forgotPassword: 'Elfelejtette...?'     // "you" formal
  }
}

Consistency Rule ​

Every key in formal must have an informal equivalent and vice versa.

The check:locale-consistency script enforces this:

bash
yarn check:locale-consistency

If you add a key to one variant, add it to the other:

typescript
// ✅ Correct - both variants have the key
const huInformalOverrides = {
  myFeature: { action: 'Csináld' }  // informal
}

const huFormalOverrides = {
  myFeature: { action: 'Tegye' }    // formal
}

// ❌ Wrong - only formal has it
const huFormalOverrides = {
  myFeature: { action: 'Tegye' }
}
// Missing in informal - check will fail!

Translation Utilities ​

getTranslation() ​

The core translation function in src/runtime/utils/locale.ts:

typescript
export const getTranslation = (
  key: string,
  locale: string = 'en',
  customTexts?: Record<string, LocaleMessages>,
  fallbackLocale: string = 'en',
  params?: (string | number)[]
): string

Features:

  • Dot notation key lookup (login.title)
  • Fallback chain: current locale → fallback locale → English
  • Parameter replacement ({0}, {1}, etc.)
  • Custom text merging with defaults
  • Development-mode parameter count validation

deepMerge() ​

Merges custom translations with defaults:

typescript
export const deepMerge = (
  target: LocaleMessages,
  source: LocaleMessages
): LocaleMessages

Used to merge user-provided custom translations with module defaults.

getNestedValue() ​

Retrieves nested values using dot notation:

typescript
export const getNestedValue = (
  obj: LocaleMessages,
  path: string
): string | undefined

Example:

typescript
getNestedValue(messages, 'login.title')
// Returns messages.login.title

Testing Translations ​

Unit Tests ​

Test individual translation functions:

typescript
import { describe, it, expect } from 'vitest'
import { getTranslation } from '../../src/runtime/utils/locale'

describe('Translations', () => {
  it('should get correct translation', () => {
    const result = getTranslation('login.title', 'en')
    expect(result).toBe('Welcome Back')
  })
  
  it('should handle parameters', () => {
    const result = getTranslation('userCard.deleteConfirm', 'en', undefined, 'en', ['John'])
    expect(result).toBe('Are you sure you want to delete user John?')
  })
  
  it('should fallback to English', () => {
    const result = getTranslation('login.title', 'nonexistent')
    expect(result).toBe('Welcome Back')  // Falls back to English
  })
})

Integration Tests ​

Test translations in components:

typescript
import { mount } from '@vue/test-utils'
import { describe, it, expect, vi } from 'vitest'
import NUsersLoginForm from '../../src/runtime/components/NUsersLoginForm.vue'

describe('NUsersLoginForm Localization', () => {
  it('should display translated strings', () => {
    const wrapper = mount(NUsersLoginForm, {
      global: {
        mocks: {
          useRuntimeConfig: () => ({
            public: {
              nuxtUsers: {
                locale: { default: 'hu', fallbackLocale: 'en', texts: {} }
              }
            }
          })
        }
      }
    })
    
    // Check Hungarian translations appear
    expect(wrapper.text()).toContain('Üdvözöljük')
  })
})

Parameter Count Validation ​

In development mode, the system validates parameter counts:

typescript
// Translation
deleteConfirm: 'Delete {0}?'

// Correct usage
t('deleteConfirm', ['John'])  // ✅ 1 param expected, 1 provided

// Incorrect usage
t('deleteConfirm', ['John', 'Extra'])  // ⚠️  Warns: expected 1, got 2
t('deleteConfirm')                      // ⚠️  Warns: expected 1, got 0

Warning example:

[nuxt-users locale] Parameter count mismatch for key "deleteConfirm": 
expected 1 parameter(s), but received 2

This only runs in development (NODE_ENV !== 'production').

Best Practices ​

1. Always Test New Translations ​

bash
# Run unit tests
yarn test:unit test/unit/locale.test.ts

# Run integration tests
yarn test:unit test/integration/locale-components.test.ts

# Check for hardcoded strings
yarn check:translations

# Check Hungarian consistency
yarn check:locale-consistency

2. Keep Keys Organized ​

Use clear, nested structure:

typescript
// ✅ Good structure
{
  feature: {
    subsection: {
      specificKey: 'value'
    }
  }
}

// ❌ Avoid flat structure
{
  featureSubsectionSpecificKey: 'value'
}

3. Document New Keys ​

When adding keys, update:

  • User guide documentation (docs/user-guide/localization.md)
  • This developer guide if adding new sections
  • JSDoc comments in type definitions

4. Use Consistent Terminology ​

Maintain consistency across languages:

  • Use the same term for the same concept
  • Keep button labels consistent (e.g., always "Submit" not mixed with "Send")
  • Follow established patterns in existing translations

5. Consider Context ​

Especially for Hungarian formal/informal:

  • Login/register forms: Can be friendly (informal) or professional (formal)
  • Admin panels: Usually formal
  • User profiles: Depends on your app's tone
  • Error messages: Keep consistent with rest of app

Continuous Integration ​

The translation checks run automatically in CI:

bash
# In package.json "release" script
yarn check:translations && yarn check:locale-consistency && yarn test

This ensures:

  • No hardcoded strings sneak into components
  • Hungarian variants stay in sync
  • All translation tests pass

Released under the MIT License.