Hone logo
Hone
Problems

React Hook for User Language Preference

This challenge focuses on creating a reusable React hook that allows applications to easily manage and access the user's preferred language. This is crucial for internationalization (i18n) and localization (l10n), ensuring a tailored experience for users across different regions.

Problem Description

You need to develop a custom React hook named usePreferredLanguage that provides a way to:

  1. Determine the user's preferred language: This should ideally default to a sensible value if no preference is explicitly set or detected.
  2. Allow setting the user's preferred language: The hook should expose a function to update the user's language preference.
  3. Persist the language preference: The chosen language should be persisted so that it survives page reloads or application restarts.

Key Requirements:

  • The hook should be implemented in TypeScript.
  • It should return an object containing the current preferred language and a function to set it.
  • Persistence should be handled using localStorage for simplicity.
  • A default language should be provided if no language is found in localStorage or if the detected browser language is not supported.

Expected Behavior:

  • On initial load, the hook should attempt to read the language from localStorage.
  • If not found in localStorage, it should attempt to detect the browser's language (e.g., from navigator.language).
  • If neither is available or the detected language is not in a predefined list of supported languages, it should fall back to a predefined default language.
  • The setLanguage function should update the current language state and save it to localStorage.
  • The hook should re-render components using it whenever the language preference changes.

Edge Cases to Consider:

  • What happens if localStorage is unavailable (e.g., in private browsing modes)?
  • How should the hook handle unsupported browser language codes?
  • What if the application only supports a limited set of languages?

Examples

Example 1: Initial Load with localStorage Value

Assume the application supports "en" (English) and "es" (Spanish), with "en" as the default.

  • Scenario: localStorage contains {"preferredLanguage": "es"}.
  • Hook Usage:
    const { language, setLanguage } = usePreferredLanguage(["en", "es"], "en");
    // language will be "es"
    
  • Explanation: The hook reads "es" from localStorage and uses it as the initial language.

Example 2: Initial Load without localStorage Value, using Browser Language

Assume the application supports "en" and "fr" (French), with "en" as the default. localStorage is empty.

  • Scenario: navigator.language is "fr-FR".
  • Hook Usage:
    const { language, setLanguage } = usePreferredLanguage(["en", "fr"], "en");
    // language will be "fr"
    
  • Explanation: Since localStorage is empty, the hook detects "fr-FR" from the browser, extracts the base language code "fr", and uses it.

Example 3: Initial Load with Unsupported Browser Language or No Browser Language

Assume the application supports "en" and "de" (German), with "en" as the default. localStorage is empty.

  • Scenario 1: navigator.language is "zh-CN" (Chinese).
  • Scenario 2: navigator.language is not available or empty.
  • Hook Usage:
    const { language, setLanguage } = usePreferredLanguage(["en", "de"], "en");
    // language will be "en" (the default)
    
  • Explanation: In both scenarios, the detected browser language is not in the list of supported languages. Therefore, the hook falls back to the provided default language, "en".

Example 4: Setting a New Language

  • Scenario: The current language is "en".
  • Hook Usage:
    const { language, setLanguage } = usePreferredLanguage(["en", "es"], "en");
    
    // Later, when a user action occurs:
    setLanguage("es");
    
    // After calling setLanguage("es"):
    // language will now be "es"
    // localStorage will be updated to {"preferredLanguage": "es"}
    
  • Explanation: Calling setLanguage("es") updates the internal state and saves the new preference to localStorage. Components using the hook will re-render with the new "es" language.

Constraints

  • The hook must be written in TypeScript.
  • The function signature for usePreferredLanguage should accept an array of supported language codes (strings) and a default language code (string).
  • The hook should return an object with language: string and setLanguage: (lang: string) => void.
  • Persistence must use localStorage. If localStorage is not available, the hook should still function without error but without persistence.
  • The detected browser language should be parsed to its base language code (e.g., "en-US" -> "en").

Notes

  • Consider how you will handle the localStorage read and write operations to gracefully handle potential errors or unavailability.
  • The setLanguage function should ensure that the provided language code is one of the supported languages before saving it, or you might decide to handle invalid inputs differently (e.g., throwing an error or ignoring). The challenge implies setting to a valid supported language.
  • Think about the best way to parse the browser's language string.
  • This hook should be designed for use within functional components.
Loading editor...
typescript