Skip to main content

Country Infos

Country Information System

The country infos system provides standardized country data with localized names and telephone prefixes across the application. It supports multiple languages with fallback mechanisms for reliability.

Data Sources

Static Country Data

Static country information is provided in two forms:

  1. Generic List (COUNTRY_INFOS_GENERIC)

    • Comprehensive list of all countries in English
    • Used as a fallback when language-specific data is unavailable
    • Contains country ID, country name, and telephone prefix
  2. Language-Specific Lists (COUNTRY_INFOS_BY_LANGUAGE)

    • Translated country names for supported languages
    • Currently available for: Japanese (ja), Korean (ko)
    • User's country is automatically prioritized at the top of the list
    • Falls back to generic list for unsupported languages

Dynamic Country Data

Country data can be fetched from the Locci API service for the most up-to-date information. This includes:

  • Latest country translations
  • Updated sorting preferences
  • Real-time maintenance by the Locci team

Basic Usage

useStandardizedCountryInfos Hook

Use this hook to retrieve country information in your components:

import { useStandardizedCountryInfos } from '@traveloka/core';

function CountrySelector() {
const countryInfos = useStandardizedCountryInfos();

return (
<select>
{countryInfos.map(country => (
<option key={country.countryId} value={country.countryId}>
{country.countryName} (+{country.telPref})
</option>
))}
</select>
);
}

The hook returns an array of CountryInfo objects with the following structure:

type CountryInfo = {
countryId: string; // ISO 3166-1 alpha-2 code (e.g., 'JP', 'US')
countryName: string; // Localized country name
telPref: string; // Telephone prefix/country code
};

Server-Side Integration

Enabling Dynamic Country Data

To retrieve country information from the Locci API service, you must enable the withCountryInfos option in your page's server function.

Using withTravelokaApp

import { withTravelokaApp } from '@traveloka/core';

const HomePage = ({ countryInfos }) => {
// Component code
};

HomePage.withCountryInfos = true;

export default withTravelokaApp(HomePage);

Using getServerSideProps

import { prepareCountryContext, getSharedServerProps } from '@traveloka/core';

export const getServerSideProps = async ctx => {
const sharedPageProps = await getSharedServerProps(
prepareCountryContext(ctx)
);

return {
props: { ...sharedPageProps },
};
};

Using cacheableServerProps

import { cacheableServerProps } from '@traveloka/core';

export const getStaticProps = cacheableServerProps({
withCountryInfos: true,
// ... other options
});

Fallback Behavior

If the Locci API request fails or the option is not enabled:

  • The system automatically falls back to static country data
  • Language-specific translations are applied when available
  • User's country is pinned to the top of the list
info

Enabling country infos data from the server increases HTML size by approximately 3KB.

Utility Functions

getStaticCountryInfos(lang: string)

Retrieve static country information for a specific language:

import { getStaticCountryInfos } from '@traveloka/core';

const countryList = getStaticCountryInfos('ja'); // Gets Japanese country names

prioritizedCountryInfoSort(options)

Pin user's country to the top of the country list:

import { prioritizedCountryInfoSort } from '@traveloka/core';

const sorted = prioritizedCountryInfoSort({
countryInfoList: countryList,
countryId: 'JP', // User's country
});

Parameters:

  • countryInfoList: Array of country infos to sort
  • countryId (optional): Country code to prioritize. If not found or undefined, the list is returned as-is

Returns: A new array with the prioritized country at the top

prepareCountryContext(ctx, enabled)

Prepare the Next.js context with country infos option:

import { prepareCountryContext } from '@traveloka/core';

export const getServerSideProps = async ctx => {
const ctxWithCountry = prepareCountryContext(ctx, true);
// Pass ctxWithCountry to your server functions
};

Caching

Country information from the Locci API is cached for 4 hours per language tag to optimize performance and reduce API calls. The cache is maintained at the service instance level.

Supported Languages (Static List)

LanguageLanguage CodeStatus
JapanesejaTranslated
KoreankoTranslated
EnglishenGeneric (Fallback)
Other-Generic (Fallback)

To add support for a new language:

  1. Add translated country names to COUNTRY_INFOS_BY_LANGUAGE in constants.ts
  2. Sort alphabetically by country name with the user's country at the top
  3. Ensure all country IDs match the generic list

TypeScript Types

type CountryInfo = {
countryId: string;
countryName: string;
telPref: string;
};

type GetServerContextWithCountryOptions = GetServerSidePropsContext & {
withCountryInfos?: boolean;
};

Best Practices

  1. Always use the hook in components - Use useStandardizedCountryInfos instead of manually managing country data
  2. Enable server-side loading when possible - Use withCountryInfos: true to get the most up-to-date data from Locci
  3. Handle missing API gracefully - The system automatically falls back to static data if Locci API is unavailable
  4. Pin user's country - User's country is automatically pinned to the top for better UX
  5. Cache considerations - Be aware that cached data may be up to 4 hours old

Common Patterns

Displaying Country Selection with Flags

import { useStandardizedCountryInfos } from '@traveloka/core';

function CountrySelectWithFlag() {
const countryInfos = useStandardizedCountryInfos();

return (
<ul>
{countryInfos.map(country => (
<li key={country.countryId}>
<span role="img" aria-label={country.countryName}>
{/* Flag emoji or icon */}
</span>
{country.countryName} (+{country.telPref})
</li>
))}
</ul>
);
}

Phone Number Input with Country Code

import { useStandardizedCountryInfos } from '@traveloka/core';
import { useState } from 'react';

function PhoneInput() {
const countryInfos = useStandardizedCountryInfos();
const [selectedCountry, setSelectedCountry] = useState(countryInfos[0]);

return (
<div>
<select
value={selectedCountry.countryId}
onChange={e => {
const selected = countryInfos.find(
c => c.countryId === e.target.value
);
setSelectedCountry(selected!);
}}
>
{countryInfos.map(country => (
<option key={country.countryId} value={country.countryId}>
+{country.telPref} {country.countryName}
</option>
))}
</select>
<input type="tel" placeholder={`+${selectedCountry.telPref}`} />
</div>
);
}