Sistema de i18n com TypeScript e intellisense

10 min de leitura

Implementando internacionalização com tipagem estática que oferece autocomplete completo para todas as chaves de tradução.

TypeScripti18nNext.jsDeveloper Experience

Sistema de i18n com TypeScript e intellisense#

Vou te mostrar como criar um sistema de internacionalização que oferece intellisense completo para todas as chaves de tradução.

O Problema#

Sistemas de i18n tradicionais têm problemas:

  • Chaves de tradução são strings "mágicas"
  • Sem autocomplete
  • Erros só aparecem em runtime
  • Refactoring é perigoso

A Solução: TypeScript + Estrutura Inteligente#

1. Estrutura JSON Organizada#

// locales/en.json
{
  "common": {
    "name": "Gabriel Albuquerque",
    "intro": "I'm a developer & designer based in Curitiba, Brazil 🇧🇷"
  },
  "experiences": {
    "title": "Experience",
    "exampleCompany": "Example Company",
    "exampleCompanyDesc": "Building modern web applications..."
  },
  "education": {
    "title": "Education",
    "exampleInstitution": "Example Institution"
  }
}

2. Tipos TypeScript Automáticos#

// types/i18n.ts
type Paths<T> = T extends object
  ? {
      [K in keyof T]: K extends string
        ? T[K] extends object
          ? `${K}.${Paths<T[K]>}`
          : K
        : never;
    }[keyof T]
  : never;

type TranslationKey = Paths<typeof enTranslations>;
// Result: "common.name" | "common.intro" | "experiences.title" | ...

3. Context com Dot Notation#

// contexts/language-context.tsx
function getNestedValue(obj: any, path: string): string {
  return path.split(".").reduce((current, key) => current?.[key], obj) || path;
}

const t = (key: TranslationKey): string => {
  const translation = translations[language];
  return getNestedValue(translation, key);
};

4. Hook Tipado#

export function useLanguage() {
  const context = useContext(LanguageContext);
  if (!context) {
    throw new Error("useLanguage must be used within LanguageProvider");
  }

  return {
    ...context,
    t: context.t as (key: TranslationKey) => string,
  };
}

Resultado: Intellisense Completo!#

// Em qualquer componente:
const { t } = useLanguage();

// ✨ Autocomplete funciona!
t("experiences.title"); // ✅
t("education.exampleInstitution"); // ✅
t("invalid.key"); // ❌ TypeScript error!

Benefícios#

🔧 Developer Experience#

  • Autocomplete completo - Vê todas as chaves disponíveis
  • Erro em compile time - Chaves inválidas são detectadas
  • Refactoring seguro - Find & replace funciona
  • Organização clara - Estrutura hierárquica

🚀 Performance#

  • Tree shaking - Apenas traduções usadas
  • Tipo checking - Zero overhead em runtime
  • Estrutura eficiente - Acesso O(1) com dot notation

Implementação Avançada#

1. Suporte a Plurais#

type PluralKey = {
  one: string;
  other: string;
};

// Usage: t('items.count', { count: 5 })

2. Interpolação#

const t = (key: TranslationKey, vars?: Record<string, string>): string => {
  let translation = getNestedValue(translations[language], key);

  if (vars) {
    Object.entries(vars).forEach(([key, value]) => {
      translation = translation.replace(`{{${key}}}`, value);
    });
  }

  return translation;
};

3. Fallback Inteligente#

const getNestedValue = (obj: any, path: string): string => {
  const value = path.split(".").reduce((current, key) => current?.[key], obj);

  if (!value && language !== "en") {
    // Fallback to English
    return getNestedValue(translations.en, path);
  }

  return value || path;
};

Conclusão#

Esse sistema transforma i18n de uma tarefa propensa a erros em uma experiência de desenvolvimento fluida e segura.

Key takeaways:

  • TypeScript pode ser usado para criar sistemas type-safe incríveis
  • Dot notation + nested objects = organização perfeita
  • Developer experience importa tanto quanto user experience

Agora toda vez que você digitar t(' vai ver todas as opções disponíveis. É mágico! ✨