Files
2026-02-18 01:31:41 +01:00

16 KiB

Phase 14: Translation Polish & Testing - Research

Researched: 2026-01-29 Domain: i18n Polish translations, pluralization testing, translation coverage audit Confidence: HIGH

Summary

This phase completes Polish translations for all Vue i18n strings and validates the bilingual experience through manual testing and automated pluralization verification. The codebase has 1858 translation keys in both en.json and pl.json that are currently synchronized, but some Polish translations may be placeholders (English strings as values). The primary work involves: (1) generating a coverage report to identify untranslated strings, (2) reviewing/fixing Polish translations including proper pluralization, (3) adding Vitest unit tests for Polish pluralization rules, and (4) manual walkthrough testing in Polish.

Key findings:

  1. Translation files are synchronized - Both en.json and pl.json have 1858 keys, but some PL values may be English placeholders
  2. Polish pluralization is configured - The i18n.config.ts has a custom pluralizationRules.pl function implementing 4-form rules
  3. 18 plural strings exist - Grep found 18 keys using pipe | separator for pluralization
  4. No unit test infrastructure - The project uses Playwright for E2E but has no Vitest setup for unit tests
  5. CLDR Polish rules are complex - Polish has 4 forms: one, few, many, other with specific mathematical rules for each

Primary recommendation: Create a simple Node.js coverage report script (not a full npm package), add Vitest with minimal setup for pluralization unit tests, generate side-by-side review markdown for translation audit, then execute manual walkthrough testing.

Standard Stack

Core

Library Version Purpose Why Standard
@nuxt/test-utils latest Nuxt test utilities Official Nuxt testing library
vitest latest Unit test runner Default for Nuxt 3, fast, TypeScript native
Node.js fs module built-in JSON file comparison No external deps for simple script

Supporting

Library Version Purpose When to Use
happy-dom latest DOM environment for Vitest Only if testing components (not needed for pure function tests)

Alternatives Considered

Instead of Could Use Tradeoff
Custom Node script i18n-check npm Overkill for 2-file comparison, adds dependency
Vitest Jest Jest requires more config for ESM/TypeScript
Manual markdown review Spreadsheet Markdown stays in repo, version controlled

Installation:

cd vue-queststream-app
pnpm add -D @nuxt/test-utils vitest

Architecture Patterns

vue-queststream-app/
├── i18n/
│   ├── locales/
│   │   ├── en.json
│   │   └── pl.json
│   └── i18n.config.ts        # Pluralization rules
├── scripts/
│   └── i18n-coverage.ts      # Coverage report script
├── tests/
│   └── i18n/
│       └── polish-pluralization.test.ts  # Pluralization unit tests
└── vitest.config.ts          # Vitest configuration

Pattern 1: Translation Coverage Report Script

What: Node.js script to compare en.json vs pl.json When to use: Before translation review, in CI optionally Example:

// scripts/i18n-coverage.ts
import en from '../i18n/locales/en.json'
import pl from '../i18n/locales/pl.json'

const enKeys = Object.keys(en)
const plKeys = Object.keys(pl)

// Find keys where PL value equals EN value (potential untranslated)
const untranslated = enKeys.filter(key =>
  en[key] === pl[key] && !key.includes('|')  // Exclude plurals
)

console.log(`Total keys: ${enKeys.length}`)
console.log(`Potentially untranslated: ${untranslated.length}`)
untranslated.forEach(key => console.log(`  - ${key}`))

Pattern 2: Polish Pluralization Unit Tests

What: Test the custom pluralization function with comprehensive values When to use: Verify plural form selection is correct for all edge cases Example:

// tests/i18n/polish-pluralization.test.ts
import { describe, it, expect } from 'vitest'

// Polish pluralization function (extracted from i18n.config.ts)
function plPluralRule(choice: number, choicesLength: number): number {
  if (choice === 0) return 0

  const teen = choice > 10 && choice < 20
  const endsWithOne = choice % 10 === 1

  if (!teen && endsWithOne) return 1
  if (!teen && choice % 10 >= 2 && choice % 10 <= 4) return 2

  return choicesLength < 4 ? 2 : 3
}

describe('Polish pluralization rules', () => {
  // 4-form tests (one | few | many | other)
  const testCases = [
    { n: 0, expected: 0, form: 'zero' },
    { n: 1, expected: 1, form: 'one' },
    { n: 2, expected: 2, form: 'few' },
    { n: 3, expected: 2, form: 'few' },
    { n: 4, expected: 2, form: 'few' },
    { n: 5, expected: 3, form: 'many' },
    { n: 11, expected: 3, form: 'many (teen)' },
    { n: 12, expected: 3, form: 'many (teen)' },
    { n: 21, expected: 1, form: 'one (21)' },
    { n: 22, expected: 2, form: 'few (22)' },
    { n: 25, expected: 3, form: 'many' },
    { n: 100, expected: 3, form: 'many' },
    { n: 101, expected: 1, form: 'one (101)' },
    { n: 102, expected: 2, form: 'few (102)' },
    { n: 105, expected: 3, form: 'many' },
  ]

  testCases.forEach(({ n, expected, form }) => {
    it(`returns form ${expected} (${form}) for n=${n}`, () => {
      expect(plPluralRule(n, 4)).toBe(expected)
    })
  })
})

Pattern 3: Side-by-Side Translation Review File

What: Markdown table with EN | PL | Context for human review When to use: Translation audit workflow Example:

| English | Polish | Context |
|---------|--------|---------|
| Save Changes | Zapisz zmiany | Button in settings forms |
| {count} quest \| {count} quests | {count} misja \| {count} misje \| {count} misji \| {count} misji | Quest count - 4 forms: one\|few\|many\|other |
| Hi {name}! | Cześć {name}! | Child dashboard greeting |

Anti-Patterns to Avoid

  • Importing vue-i18n in unit tests: Test the pluralization function directly, not through i18n runtime
  • Using E2E for pluralization: Playwright is slow for 50+ test cases, use unit tests
  • Translating everything at once: Generate report first, review systematically
  • Skipping visual checks: Polish strings are often 20-30% longer than English

Don't Hand-Roll

Problem Don't Build Use Instead Why
JSON key comparison Full npm package Simple Node script 50 lines vs dependency
Plural rule testing Browser testing Vitest unit tests 100x faster
Translation review Manual file comparison Generated markdown table Structured, auditable
Coverage tracking Spreadsheet Script output + git diff Version controlled

Key insight: This phase is about validation, not new features. Keep tooling minimal - a script and a test file.

Common Pitfalls

Pitfall 1: Testing Pluralization Through vue-i18n Runtime

What goes wrong: Tests are slow, require full Nuxt context, flaky Why it happens: Assumption that i18n must be tested end-to-end How to avoid: Extract pluralization function, test as pure function Warning signs: Tests taking >100ms each, requiring mountSuspended

Pitfall 2: Missing Polish Plural Forms

What goes wrong: "5 misja" instead of "5 misji" (wrong form) Why it happens: Only providing 2 forms when Polish needs 4 How to avoid: Ensure all plural keys have form0 | form1 | form2 | form3 in pl.json Warning signs: Pipe-separated values with different count in EN vs PL

Pitfall 3: False Positives in Coverage Report

What goes wrong: Marking valid strings as untranslated Why it happens: Some strings are intentionally same in both languages (e.g., "QuestStream", "XP") How to avoid: Add exclusion list for proper nouns, abbreviations Warning signs: Coverage report showing brand names, technical terms

Pitfall 4: Overlooking String Length in UI

What goes wrong: Polish text truncated or breaks layout Why it happens: Polish words are longer than English equivalents How to avoid: Manual visual walkthrough of all pages in Polish Warning signs: Ellipsis (...), text overflow, button text wrapping

Pitfall 5: Inconsistent Translation Tone

What goes wrong: Mixed formal/informal Polish ("Ty" vs "Pan/Pani") Why it happens: No style guide, different translation sessions How to avoid: Review all translations together, enforce consistent tone (informal for QuestStream) Warning signs: "Zaloguj się" (formal) mixed with "Cześć!" (informal)

Pitfall 6: Incorrect Teen Number Handling

What goes wrong: "11 misja" or "12 misje" (should be "11 misji", "12 misji") Why it happens: Polish teen numbers (11-19) use "many" form, not "one"/"few" How to avoid: Test values 11-19 explicitly in unit tests Warning signs: Grammar errors specifically on 11-19

Code Examples

vitest.config.ts (Minimal Setup)

// vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    include: ['tests/**/*.{test,spec}.ts'],
    environment: 'node',  // No DOM needed for pure function tests
  },
})

Coverage Report Script

// scripts/i18n-coverage.ts
import { readFileSync } from 'fs'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'

const __dirname = dirname(fileURLToPath(import.meta.url))

interface TranslationFile {
  [key: string]: string
}

// Load translation files
const en: TranslationFile = JSON.parse(
  readFileSync(resolve(__dirname, '../i18n/locales/en.json'), 'utf-8')
)
const pl: TranslationFile = JSON.parse(
  readFileSync(resolve(__dirname, '../i18n/locales/pl.json'), 'utf-8')
)

// Strings that are intentionally the same in both languages
const EXCLUSIONS = [
  'QuestStream',
  'XP',
  'PIN',
  // Add brand names, abbreviations, etc.
]

const enKeys = Object.keys(en)
const plKeys = Object.keys(pl)

// Keys in EN but not in PL
const missingInPl = enKeys.filter(k => !(k in pl))

// Keys where PL value equals EN value (potential untranslated)
const untranslated = enKeys.filter(key => {
  if (EXCLUSIONS.includes(en[key])) return false
  if (key.includes('|')) return false  // Skip plurals (different structure)
  return en[key] === pl[key]
})

// Plural keys with mismatched form counts
const pluralMismatch = enKeys.filter(key => {
  if (!key.includes('|')) return false
  const enForms = (en[key].match(/\|/g) || []).length + 1
  const plForms = (pl[key].match(/\|/g) || []).length + 1
  return enForms !== plForms
})

console.log('\n=== i18n Coverage Report ===\n')
console.log(`Total keys: ${enKeys.length}`)
console.log(`Missing in pl.json: ${missingInPl.length}`)
console.log(`Potentially untranslated: ${untranslated.length}`)
console.log(`Plural form mismatch: ${pluralMismatch.length}`)

if (missingInPl.length > 0) {
  console.log('\n--- Missing in pl.json ---')
  missingInPl.forEach(k => console.log(`  ${k}`))
}

if (untranslated.length > 0) {
  console.log('\n--- Potentially Untranslated ---')
  untranslated.slice(0, 20).forEach(k => console.log(`  ${k}: "${en[k]}"`))
  if (untranslated.length > 20) {
    console.log(`  ... and ${untranslated.length - 20} more`)
  }
}

if (pluralMismatch.length > 0) {
  console.log('\n--- Plural Form Mismatch (EN vs PL) ---')
  pluralMismatch.forEach(k => {
    const enForms = (en[k].match(/\|/g) || []).length + 1
    const plForms = (pl[k].match(/\|/g) || []).length + 1
    console.log(`  ${k}: EN has ${enForms} forms, PL has ${plForms} forms`)
  })
}

// Exit code for CI
const hasIssues = missingInPl.length > 0 || pluralMismatch.length > 0
process.exit(hasIssues ? 1 : 0)

Running Coverage Report

npx tsx scripts/i18n-coverage.ts

Polish Pluralization Test Values

Based on CLDR rules, the comprehensive test set for Polish:

// Test values that cover all plural categories
const testValues = [
  // Zero form (index 0)
  0,

  // One form (index 1) - ends in 1, not 11
  1, 21, 31, 41, 51, 101, 121,

  // Few form (index 2) - ends in 2-4, not 12-14
  2, 3, 4, 22, 23, 24, 32, 33, 34, 102, 103, 104,

  // Many form (index 3) - everything else including teens
  5, 6, 7, 8, 9, 10,
  11, 12, 13, 14, 15, 16, 17, 18, 19,  // Teens always "many"
  20, 25, 26, 27, 28, 29, 30,
  100, 105, 111, 112  // Large numbers
]

Manual Testing Checklist Template

## Manual Polish Testing Checklist

### Auth Flow
- [ ] /login - All labels, buttons, errors in Polish
- [ ] /register - Form labels, validation messages
- [ ] Password reset flow

### Parent Dashboard
- [ ] /parent - Welcome message, stats, pending approvals
- [ ] /parent/children - Child cards, empty state
- [ ] /parent/templates - Template library, filters
- [ ] /parent/settings - All 7 tabs
- [ ] /parent/profile - All 3 tabs

### Child Dashboard
- [ ] /child - Hero greeting, stats, quests
- [ ] /child/quests - Quest cards, status labels
- [ ] /child/shop - Rewards, purchase flow
- [ ] /child/achievements - Badges, unlock dates
- [ ] /child/profile - Stats, PIN change

### Visual Checks (Polish strings often longer)
- [ ] Buttons don't overflow
- [ ] Table headers fit
- [ ] Modal titles don't truncate
- [ ] Navigation labels fit

### Pluralization Live Check
- [ ] 1 quest assigned
- [ ] 2 quests assigned
- [ ] 5 quests assigned
- [ ] 21 quests assigned (tricky!)

State of the Art

Old Approach Current Approach When Changed Impact
Jest for Vue Vitest 2023 Faster, better TypeScript support
@nuxtjs/i18n v8 @nuxtjs/i18n v10 2025 Better lazy loading, bundle optimization
Manual key audit Automated coverage scripts Always Catch regressions in CI

Deprecated/outdated:

  • vue-i18n-jest: Use vitest directly
  • nuxt-vitest: Merged into @nuxt/test-utils

Open Questions

  1. Console Warning Logging

    • What we know: vue-i18n can log fallback warnings
    • What's unclear: How to capture warnings during manual testing
    • Recommendation: Set missingWarn: true in i18n.config.ts during Polish testing, watch browser console
  2. CI Integration Scope

    • What we know: Coverage script can exit with error code
    • What's unclear: Should this block deploys or just warn?
    • Recommendation: Start as warning, consider blocking after Phase 14 complete

Polish Pluralization Rules Reference

Based on CLDR (Unicode Common Locale Data Repository):

Category Condition Examples Form Index
zero n = 0 0 0
one n % 10 = 1 AND n % 100 != 11 1, 21, 31, 101 1
few n % 10 in 2..4 AND n % 100 not in 12..14 2, 3, 4, 22, 23, 24 2
many n % 10 = 0 OR n % 10 in 5..9 OR n % 100 in 11..14 0, 5, 10, 11, 12, 13, 14, 15, 20, 25 3

Example translations:

1 misja, 2 misje, 5 misji, 11 misji, 21 misja, 22 misje
1 dzień, 2 dni, 5 dni, 11 dni, 21 dzień, 22 dni
1 dziecko, 2 dzieci, 5 dzieci, 11 dzieci, 21 dziecko, 22 dzieci

Sources

Primary (HIGH confidence)

  • Project codebase: vue-queststream-app/i18n/locales/*.json - verified key counts
  • Project codebase: vue-queststream-app/i18n/i18n.config.ts - verified pluralization rule
  • Nuxt Testing Documentation - Vitest setup
  • Vue I18n Pluralization - Pipe syntax

Secondary (MEDIUM confidence)

  • CLDR Plural Rules - Polish rule reference
  • Phase 13 RESEARCH.md - established i18n patterns
  • Phase 13 SUMMARY files - translation string counts

Tertiary (LOW confidence)

  • WebSearch for testing tools - validation needed during implementation

Metadata

Confidence breakdown:

  • Standard stack: HIGH - verified with official Nuxt docs
  • Architecture patterns: HIGH - patterns verified with codebase
  • Pluralization rules: HIGH - verified with CLDR reference
  • Testing approach: MEDIUM - Vitest setup needs validation during implementation
  • Pitfalls: HIGH - based on Phase 13 learnings

Research date: 2026-01-29 Valid until: 90 days (stable patterns, minimal ecosystem churn expected)