close
close

Building a Scalable AI Translator with Next.js Pt 1: Leveraging Composition and Custom Hooks

Building a Scalable AI Translator with Next.js Pt 1: Leveraging Composition and Custom Hooks

In summary:

  • An AI translator built using React and Next.js
  • Key architectural approaches:
    • Component composition for a modular user interface
    • Custom hooks for reusable logic
    • Separation of Concerns for Maintainability
  • Benefits: Reusability, testability, maintainability and scalability
  • Future improvements: advanced state management, performance optimizations and improved error handling

Introduction:

In modern web development, building maintainable and scalable applications is crucial. This article explores the architectural decisions and design patterns used in building an AI Translator application using React and Next.js. The focus is on how composition and custom hooks can lead to a more modular, reusable, and maintainable codebase.

Project Overview:

See the source code here ai-translator.
Validate the hash: commit 894ab7aa92a9821b888925c84fe01167d24891aa

The AI ​​Translator is a web application that:

  • Allows users to enter text
  • Provides translations in different languages
  • Has a chat-like interface
  • Uses OpenAI API for translations

Architectural approach:

1. Composition of components:

The application is structured using a composition-based approach.

Key points:

  • The main component, AiChat, is composed of smaller, focused components
  • Each component has a unique responsibility

Example:

// ai-translator/src/components/AiChat.tsx
const AiChat: React.FC = () => {
  // ... state and hooks ...

  return (
    <>
      <ChatWindow messages={messages} />
      <LanguageSelector
        selectedLanguage={selectedLanguage}
        onLanguageChange={handleLanguageChange}
      />
      <ChatInput
        value={inputValue}
        onChange={handleInputChange}
        onSubmit={onSubmit}
        isDisabled={isTranslating}
      />
    </>
  );
}
Enter full screen mode

Exit full screen mode

Benefits:

  • Better separation of concerns
  • Each component is more focused and easier to maintain
  • Improved readability and testability

2. Custom hooks:

Complex logic is abstracted into custom hooks.

Custom Key Hooks:

  • useTranslation: Handles translation API calls
  • useChat: Manages chat status and message management
  • useLanguage: Manages language selection

Example of useChat hook:

// ai-translator/src/hooks/useChat.ts
export const useChat = (selectedLanguage: string) => {
  const (messages, setMessages) = useState<Message()>(());
  const (isTranslating, setIsTranslating) = useState<boolean>(false);
  const { translateText } = useTranslation();

  const addMessage = useCallback((text: string, isUser: boolean) => {
    const newMessage: Message = { id: Date.now(), text, isUser };
    setMessages(prev => (...prev, newMessage));
  }, ());

  const handleSubmit = useCallback(async (inputValue: string) => {
    // ... translation logic ...
  }, (selectedLanguage, isTranslating, addMessage, translateText));

  return { messages, isTranslating, handleSubmit };
};
Enter full screen mode

Exit full screen mode

Benefits:

  • Separation of Concerns
  • Reusable logic between components
  • Easier testing for complex logic

3. Separation of concerns:

The application is separated into distinct layers:

  • Presentation Layer: React Components
    • Example: AiChat, TranslationInput, ChatInput
  • Application Logic Layer: Custom Hooks
    • Example: useChat, useTranslation, useLanguage
  • Data Access Layer: API Routes and Services
    • Example: translationService

Example of interaction between layers:

// Presentation Layer
const AiChat: React.FC = () => {
  // Application Logic Layer
  const { messages, handleSubmit } = useChat();

  return (
    <>
      <ChatWindow messages={messages} />
      <ChatInput onSubmit={handleSubmit} />
    </>
  );
}

// Data Access Layer (in useChat hook)
const { translateText } = useTranslation();
const translatedText = await translateText(inputValue, selectedLanguage);
Enter full screen mode

Exit full screen mode

Benefits:

  • Modular code structure
  • Easier to test and maintain
  • Clear separation of responsibilities

Advantages of this approach:

  1. Reusability:

    • Custom hooks like useTranslation can be reused in different components
    • Example: Usage useTranslation in chat and document translation features
  2. Testability:

    • Smaller, focused components and hooks are easier to unit test
    • Example: Test useChat isolated hook of UI components
  3. Maintainability:

    • Separation of concerns makes it easier to update or replace individual parts
    • Example: Replacing the translation API without affecting UI components
  4. Scalability:

    • New features can be added by creating new components and hooks
    • Example: Adding voice translation functionality by creating new components and hooks without significant impact on existing code

Other improvements:

  1. State management:

    • Implementing global state management for larger applications
    • Example: Using Redux to manage user preferences in the application
  2. Performance optimization:

    • Implement memorization techniques
    • Using Virtualization for Long Message Lists
    • Example:
     const MemoizedChatWindow = React.memo(ChatWindow);
     const optimizedMessages = useMemo(() => processMessages(messages), (messages));
    
  3. Error handling:

    • Implement a robust error handling strategy
    • Example: creation of a useError hook to handle and display errors in the application
  4. Accessibility:

    • Make sure all components are accessible
    • Example: creation of a useFocus hook to manage focus in chat interface
  5. Internationalization:

    • Implement language switching functionality for the user interface
    • Example: Using react-i18next to handle UI translations
  6. Code splitting:

    • Use Next.js’ built-in code splitting features
    • Example: Using dynamic imports for less frequently used components

Conclusion:

By leveraging composition and custom hooks, a scalable and maintainable architecture has been created for the AI ​​Translator app. This approach allows for easy extension and modification, establishing a solid foundation for future development. As the app continues to evolve, further optimizations and improvements will be focused on improving performance and user experience.