Skip to content

Commit

Permalink
Added incremental i18n support to new places page (#4849)
Browse files Browse the repository at this point in the history
Changes
* Added `hl=<lang>` param to api calls
* Translated category tab names
* Translations for "Places in" box
* Displays translated variable names

## Before (hl=fr)

![i18n_before](https://github.com/user-attachments/assets/5303008c-bb81-4fea-949c-916565e821f2)


## After (hl=fr)

![i18n_after](https://github.com/user-attachments/assets/32663679-2bd1-47bc-8378-567091b7203a)
  • Loading branch information
dwnoble authored Jan 18, 2025
1 parent 511f310 commit ff06574
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 89 deletions.
17 changes: 14 additions & 3 deletions packages/client/src/data_commons_web_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export interface DatacommonsWebClientParams {
apiRoot?: string;
}

const LOCALE_PARAM = "hl";

class DataCommonsWebClient {
/** Website API root */
apiRoot?: string;
Expand Down Expand Up @@ -213,12 +215,17 @@ class DataCommonsWebClient {
* @param params.placeDcid place dcid to fetch data for
*/
async getPlaceCharts(params: {
category?: string;
placeDcid: string;
category?: string;
locale?: string;
}): Promise<PlaceChartsApiResponse> {
const queryString = toURLSearchParams({
category: params.category,
[LOCALE_PARAM]: params.locale,
});
const url = `${this.apiRoot || ""}/api/dev-place/charts/${
params.placeDcid
}${params.category ? "?category=" + params.category : ""}`;
}?${queryString}`;
const response = await fetch(url);
return (await response.json()) as PlaceChartsApiResponse;
}
Expand All @@ -230,10 +237,14 @@ class DataCommonsWebClient {
*/
async getRelatedPLaces(params: {
placeDcid: string;
locale?: string;
}): Promise<RelatedPlacesApiResponse> {
const queryString = toURLSearchParams({
[LOCALE_PARAM]: params.locale,
});
const url = `${this.apiRoot || ""}/api/dev-place/related-places/${
params.placeDcid
}`;
}?${queryString}`;
const response = await fetch(url);
return (await response.json()) as RelatedPlacesApiResponse;
}
Expand Down
11 changes: 8 additions & 3 deletions packages/client/src/data_commons_web_client_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export type ObservationDatesApiResponse = {
type ChartType = "BAR" | "LINE" | "MAP" | "RANKING" | "HIGHLIGHT";
export interface Chart {
type: ChartType;
maxPlaces?: number;
maxPlaces?: number;
}

export interface Place {
Expand All @@ -158,7 +158,7 @@ export interface Place {
}

export interface BlockConfig {
charts: Chart[]
charts: Chart[];
childPlaceType: string;
childPlaces: Place[];
nearbyPlaces: Place[];
Expand All @@ -175,13 +175,18 @@ export interface BlockConfig {
scaling?: number; // Optional
}

export interface Category {
name: string;
translatedName: string;
}

/**
* Website API response for /api/dev-place/charts/<place_dcid>
*/
export interface PlaceChartsApiResponse {
blocks: BlockConfig[];
place: Place;
translatedCategoryStrings: Record<string, string>;
categories: Category[];
}

/**
Expand Down
30 changes: 13 additions & 17 deletions server/routes/dev_place/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@
from server.routes.dev_place.types import PlaceChartsApiResponse
from server.routes.dev_place.types import RelatedPlacesApiResponse

OVERVIEW_CATEGORY = "Overview"
CATEGORIES = {
OVERVIEW_CATEGORY, "Economics", "Health", "Equity", "Crime", "Education",
"Demographics", "Housing", "Environment", "Energy"
}

# Define blueprint
bp = Blueprint("dev_place_api", __name__, url_prefix='/api/dev-place')

Expand All @@ -58,15 +52,18 @@ def place_charts(place_dcid: str):
- Charts specific to the place
- Translated category strings for the charts
"""

# Ensure category is valid
place_category = request.args.get("category", OVERVIEW_CATEGORY)
parent_place_dcid = place_utils.get_place_override(
place_utils.get_parent_places(place_dcid))
if place_category not in CATEGORIES:
place_category = request.args.get("category", place_utils.OVERVIEW_CATEGORY)
if place_category not in place_utils.CATEGORIES:
return error_response(
f"Argument 'category' {place_category} must be one of: {', '.join(CATEGORIES)}"
f"Argument 'category' {place_category} must be one of: {', '.join(place_utils.CATEGORIES)}"
)

# Get parent place DCID
parent_place_dcid = place_utils.get_place_override(
place_utils.get_parent_places(place_dcid))

# Fetch place info
place = place_utils.fetch_place(place_dcid, locale=g.locale)

Expand Down Expand Up @@ -98,14 +95,13 @@ def place_charts(place_dcid: str):
blocks = place_utils.chart_config_to_overview_charts(translated_chart_config,
child_place_type)

# Translate category strings for all charts that have any data.
translated_category_strings = place_utils.get_translated_category_strings(
# Translate category strings
categories_with_translations = place_utils.get_categories_with_translations(
chart_config_existing_data)

response = PlaceChartsApiResponse(
blocks=blocks,
place=place,
translatedCategoryStrings=translated_category_strings)
response = PlaceChartsApiResponse(blocks=blocks,
place=place,
categories=categories_with_translations)
return jsonify(response)


Expand Down
8 changes: 7 additions & 1 deletion server/routes/dev_place/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,20 @@ class Place:
dissolved: bool = False


@dataclass
class Category:
name: str
translatedName: str


@dataclass
class PlaceChartsApiResponse:
"""
API Response for /api/dev-place/charts/<place_dcid>
"""
blocks: List[BlockConfig]
place: Place
translatedCategoryStrings: Dict[str, str]
categories: List[Category]


@dataclass
Expand Down
46 changes: 40 additions & 6 deletions server/routes/dev_place/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from server.lib.i18n import DEFAULT_LOCALE
from server.routes import TIMEOUT
from server.routes.dev_place.types import BlockConfig
from server.routes.dev_place.types import Category
from server.routes.dev_place.types import Chart
from server.routes.dev_place.types import Place
from server.routes.dev_place.types import ServerBlockMetadata
Expand All @@ -46,6 +47,14 @@
'Continent',
]

# Place page categories
OVERVIEW_CATEGORY = "Overview"
ORDERED_CATEGORIES = [
OVERVIEW_CATEGORY, "Economics", "Health", "Equity", "Crime", "Education",
"Demographics", "Housing", "Environment", "Energy"
]
CATEGORIES = set(ORDERED_CATEGORIES)


def get_place_html_link(place_dcid: str, place_name: str) -> str:
"""Get <a href-place page url> tag linking to the place page for a place
Expand Down Expand Up @@ -580,18 +589,43 @@ def translate_chart_config(chart_config: List[ServerChartConfiguration]):
return translated_chart_config


def get_translated_category_strings(
def get_categories_with_translations(
chart_config: List[ServerChartConfiguration]) -> Dict[str, str]:
translated_category_strings: Dict[str, str] = {}
"""
Returns a list of categories with their translated names from the chart config.
Args:
chart_config (List[ServerChartConfiguration]): The chart configuration to use for determining categories.
Returns:
List[Category]: A list of categories with their translated names.
"""
categories: List[Category] = []

overview_category = Category(
name=OVERVIEW_CATEGORY,
translatedName=get_translated_category_string(OVERVIEW_CATEGORY))
categories.append(overview_category)

categories_set: Set[str] = set()
for page_config_item in chart_config:
category = page_config_item.category
if category in translated_category_strings:
if category in categories_set:
continue
categories_set.add(category)

for category in ORDERED_CATEGORIES:
if not category in categories_set:
continue
translated_category_strings[category] = gettext(
f'CHART_TITLE-CHART_CATEGORY-{category}')
category = Category(name=category,
translatedName=get_translated_category_string(category))
categories.append(category)

return categories


return translated_category_strings
def get_translated_category_string(category: str) -> str:
return gettext(f'CHART_TITLE-CHART_CATEGORY-{category}')


def get_place_cohort(place: Place) -> str:
Expand Down
11 changes: 7 additions & 4 deletions server/tests/routes/api/dev_place_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def test_dev_place_charts(self, mock_obs_point_within, mock_obs_point,
response_json = response.get_json()
self.assertIn('blocks', response_json)
self.assertIn('place', response_json)
self.assertIn('translatedCategoryStrings', response_json)
self.assertIn('categories', response_json)
self.assertIn('charts', response_json['blocks'][0])

# Check that the 'charts' field contains the expected number of charts
Expand All @@ -94,9 +94,12 @@ def test_dev_place_charts(self, mock_obs_point_within, mock_obs_point,
self.assertEqual(response_json['place']['name'], 'United States')
self.assertEqual(response_json['place']['types'], ['Country'])

# Check that 'translatedCategoryStrings' contains expected categories
self.assertIn('Crime', response_json['translatedCategoryStrings'])
self.assertIn('Education', response_json['translatedCategoryStrings'])
# Check that 'categories' contains expected categories
categories = [
category['translatedName'] for category in response_json['categories']
]
self.assertIn('Crime', categories)
self.assertIn('Education', categories)

# Ensure the denominator is present in chart results
self.assertEqual(1, len(response_json["blocks"][0]["denominator"]))
Expand Down
6 changes: 2 additions & 4 deletions server/webdriver/tests/place_explorer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

from server.routes.dev_place.utils import ORDERED_CATEGORIES
from server.webdriver import base_utils
from server.webdriver import shared
from server.webdriver.base_dc_webdriver import BaseDcWebdriverTest
Expand All @@ -30,10 +31,7 @@ def test_dev_place_overview_california(self):
"""Ensure experimental dev place page content loads"""
self.driver.get(self.url_ + '/place/geoId/06?force_dev_places=true')

expected_topics = [
"Overview", "Crime", "Demographics", "Economics", "Education", "Energy",
"Environment", "Equity", "Health", "Housing"
]
expected_topics = ORDERED_CATEGORIES
shared.assert_topics(self,
self.driver,
path_to_topics=['explore-topics-box'],
Expand Down
14 changes: 4 additions & 10 deletions static/js/place/child_places_menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import React from "react";

import { intl, LocalizedLink } from "../i18n/i18n";
import { displayNameForPlaceType } from "./util";
import { displayNameForPlaceType, pageMessages } from "./util";

interface ChildPlacePropType {
childPlaces: { string: string[] };
Expand All @@ -32,15 +32,9 @@ class ChildPlace extends React.Component<ChildPlacePropType> {
return (
<React.Fragment>
<span id="child-place-head">
{intl.formatMessage(
{
id: "child_places_menu-places_in_place",
defaultMessage: "Places in {placeName}",
description:
'Used for the child places navigation sidebar. Shows a list of place contained in the current place. For example, the sidebar for the Austria place page shows links to child places under the header "Places in {Austria}".',
},
{ placeName: this.props.placeName }
)}
{intl.formatMessage(pageMessages.placesInPlace, {
placeName: this.props.placeName,
})}
</span>
{Object.keys(this.props.childPlaces)
.sort()
Expand Down
6 changes: 5 additions & 1 deletion static/js/place/dev_place.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ window.addEventListener("load", async (): Promise<void> => {
const locale = metadataContainer.dataset.locale;

// Load locale data
await loadLocaleData(locale, []);
await loadLocaleData(locale, [
import(`../i18n/compiled-lang/${locale}/place.json`),
import(`../i18n/compiled-lang/${locale}/stats_var_labels.json`),
import(`../i18n/compiled-lang/${locale}/units.json`),
]);

// Render page
renderPage();
Expand Down
Loading

0 comments on commit ff06574

Please sign in to comment.