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:
- Determine the user's preferred language: This should ideally default to a sensible value if no preference is explicitly set or detected.
- Allow setting the user's preferred language: The hook should expose a function to update the user's language preference.
- 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
localStoragefor simplicity. - A default language should be provided if no language is found in
localStorageor 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., fromnavigator.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
setLanguagefunction should update the current language state and save it tolocalStorage. - The hook should re-render components using it whenever the language preference changes.
Edge Cases to Consider:
- What happens if
localStorageis 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:
localStoragecontains{"preferredLanguage": "es"}. - Hook Usage:
const { language, setLanguage } = usePreferredLanguage(["en", "es"], "en"); // language will be "es" - Explanation: The hook reads "es" from
localStorageand 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.languageis "fr-FR". - Hook Usage:
const { language, setLanguage } = usePreferredLanguage(["en", "fr"], "en"); // language will be "fr" - Explanation: Since
localStorageis 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.languageis "zh-CN" (Chinese). - Scenario 2:
navigator.languageis 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
languageis "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 tolocalStorage. Components using the hook will re-render with the new "es" language.
Constraints
- The hook must be written in TypeScript.
- The function signature for
usePreferredLanguageshould accept an array of supported language codes (strings) and a default language code (string). - The hook should return an object with
language: stringandsetLanguage: (lang: string) => void. - Persistence must use
localStorage. IflocalStorageis 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
localStorageread and write operations to gracefully handle potential errors or unavailability. - The
setLanguagefunction 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.