Claude Code Plugins

Community-maintained marketplace

Feedback

Enforces internationalization best practices for Breath of Now. Use this skill when creating or editing any component, page, or UI element. Ensures NO hardcoded text and proper use of next-intl translations.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name i18n-enforcer
description Enforces internationalization best practices for Breath of Now. Use this skill when creating or editing any component, page, or UI element. Ensures NO hardcoded text and proper use of next-intl translations.

i18n Enforcer Skill

Este skill garante que todo o texto visível ao utilizador no projecto Breath of Now está devidamente internacionalizado. ZERO texto hardcoded é aceitável.

Quando Usar

Aplica automaticamente este skill quando:

  • Criar novos componentes ou páginas
  • Editar elementos UI existentes
  • Adicionar mensagens de erro, labels, ou qualquer texto
  • Rever código para compliance de i18n

Regras Core

1. NUNCA Hardcodes Texto

// ❌ ERRADO:
<h1>Welcome to Breath of Now</h1>
<button>Sign In</button>
<p>Loading...</p>
<span aria-label="Close menu">×</span>

// ✅ CORRECTO:
const t = useTranslations('HomePage');

<h1>{t('title')}</h1>
<button>{t('signIn')}</button>
<p>{t('loading')}</p>
<span aria-label={t('closeMenu')}>×</span>

2. Sempre Usar useTranslations Hook

import { useTranslations } from 'next-intl';

export function MyComponent() {
  const t = useTranslations('MyComponent');
  
  return <div>{t('welcomeMessage')}</div>;
}

3. Estrutura de Ficheiros de Tradução

Localização: /messages/{locale}.json

Locales suportados:

  • en.json - English (primário)
  • pt.json - Português
  • es.json - Español
  • fr.json - Français

4. Organização de Namespaces

{
  "common": {
    "loading": "Loading...",
    "error": "An error occurred",
    "save": "Save",
    "cancel": "Cancel",
    "delete": "Delete",
    "edit": "Edit"
  },
  "nav": {
    "home": "Home",
    "pricing": "Pricing",
    "account": "Account"
  },
  "expenses": {
    "title": "ExpenseFlow",
    "addExpense": "Add Expense",
    "categories": "Categories"
  },
  "fitlog": {
    "title": "FitLog",
    "startWorkout": "Start Workout",
    "history": "History"
  }
}

5. Valores Dinâmicos

// No componente
const t = useTranslations('Dashboard');
<p>{t('greeting', { name: user.name })}</p>
<p>{t('lastSync', { date: formatDate(lastSyncDate) })}</p>

// No ficheiro de tradução
{
  "Dashboard": {
    "greeting": "Hello, {name}!",
    "lastSync": "Last synced: {date}"
  }
}

6. Pluralização

// No componente
<p>{t('itemCount', { count: items.length })}</p>
<p>{t('daysRemaining', { count: daysLeft })}</p>

// No ficheiro de tradução
{
  "itemCount": "{count, plural, =0 {No items} =1 {1 item} other {# items}}",
  "daysRemaining": "{count, plural, =0 {Last day!} =1 {1 day remaining} other {# days remaining}}"
}

Checklist de Verificação

Antes de completar qualquer tarefa, verifica:

  • Nenhuma string hardcoded em JSX/TSX
  • Todo o texto usa hook useTranslations()
  • Keys de tradução adicionadas a TODOS os 4 ficheiros de locale
  • Namespace corresponde ao nome do componente/página
  • Valores dinâmicos usam interpolação adequada
  • Pluralização tratada onde necessário
  • Alt text de imagens também traduzido
  • Aria-labels traduzidos

Erros Comuns

Erro 1: Esquecer alt text

// ❌ ERRADO
<img src={logo} alt="Logo" />

// ✅ CORRECTO
<img src={logo} alt={t('logoAlt')} />

Erro 2: Hardcoded aria-labels

// ❌ ERRADO
<button aria-label="Close menu">X</button>

// ✅ CORRECTO
<button aria-label={t('closeMenu')}>X</button>

Erro 3: Placeholders hardcoded

// ❌ ERRADO
<Input placeholder="Enter your email" />

// ✅ CORRECTO
<Input placeholder={t('emailPlaceholder')} />

Erro 4: Títulos de página hardcoded

// ❌ ERRADO
<title>ExpenseFlow - Breath of Now</title>

// ✅ CORRECTO
<title>{t('pageTitle')}</title>

Erro 5: Mensagens de erro hardcoded

// ❌ ERRADO
toast.error('Failed to save');

// ✅ CORRECTO
toast.error(t('errors.saveFailed'));

Permitido: Console messages

// ✅ OK - mensagens de console não precisam tradução
console.log('Component mounted');
console.error('Failed to fetch data:', error);

Comandos de Detecção Rápida

Para encontrar texto hardcoded no codebase:

# Encontrar potenciais strings hardcoded em ficheiros TSX
grep -r ">[A-Z][a-z]" --include="*.tsx" src/

# Encontrar strings que podem estar hardcoded
grep -rn '"[A-Z][a-zA-Z ]{3,}"' --include="*.tsx" src/

# Encontrar elementos com text content directo
grep -rn ">[a-zA-Z].*<" --include="*.tsx" src/

Padrão de Implementação

Quando criar um novo componente:

Passo 1: Definir namespace no ficheiro

'use client';

import { useTranslations } from 'next-intl';

export function NewComponent() {
  const t = useTranslations('NewComponent');
  
  return (
    <div>
      <h2>{t('title')}</h2>
      <p>{t('description')}</p>
    </div>
  );
}

Passo 2: Adicionar traduções a TODOS os locales

// messages/en.json
{
  "NewComponent": {
    "title": "Component Title",
    "description": "Component description here"
  }
}

// messages/pt.json
{
  "NewComponent": {
    "title": "Título do Componente",
    "description": "Descrição do componente aqui"
  }
}

// Repetir para es.json, fr.json

Passo 3: Verificar

# Verificar que não há texto hardcoded
grep -n ">[A-Z]" src/components/new-component.tsx

Prioridade

Este skill tem PRIORIDADE MÁXIMA. Nenhum PR ou mudança de código deve ser aceite sem implementação adequada de i18n. Este é um princípio core da acessibilidade global do Breath of Now.

Ficheiros de Referência

Verifica sempre:

  • /messages/en.json - Traduções principais
  • .claude/commands/i18n-check.md - Comando de verificação
  • Componentes existentes para padrões de uso

REGRA DE OURO: Se um utilizador vai ver o texto, esse texto DEVE ser traduzido. Sem excepções.