Skip to content

Commit 16156cf

Browse files
Adding Translations for API Documentation Content (#400)
* created centralized doc * adding content to views * adding filter content * adding unit tests * adding translation functionality * adding documentation * fixing failing tests
1 parent a3407d5 commit 16156cf

File tree

5 files changed

+221
-166
lines changed

5 files changed

+221
-166
lines changed

backend/app/settings.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020

2121
from app.logging import sql_trace_formatter
2222

23+
LANGUAGE_CODE = "en-us"
24+
25+
LANGUAGES = [
26+
("en", ("English")),
27+
]
28+
2329
# Build paths inside the project like this: BASE_DIR / 'subdir'.
2430
BASE_DIR = Path(__file__).resolve().parent.parent
2531

@@ -41,6 +47,10 @@
4147

4248
INTERNAL_APIS = config("DJANGO_ALLOWED_HOSTS").split(",")
4349

50+
LOCALE_PATHS = [
51+
os.path.join(BASE_DIR, "locale"),
52+
]
53+
4454
# Application definition
4555

4656
INSTALLED_APPS = [
@@ -73,6 +83,7 @@
7383
"django.middleware.security.SecurityMiddleware",
7484
"whitenoise.middleware.WhiteNoiseMiddleware",
7585
"django.contrib.sessions.middleware.SessionMiddleware",
86+
"django.middleware.locale.LocaleMiddleware",
7687
"django.middleware.common.CommonMiddleware",
7788
"django.middleware.csrf.CsrfViewMiddleware",
7889
"django.contrib.auth.middleware.AuthenticationMiddleware",

backend/locale/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Localization
2+
3+
The NPD API documentation supports internationalization via Django's translation framework. All API documentation strings in `documentation_content.py` are wrapped in `gettext_lazy` so they are translation ready.
4+
5+
Currently, only English is supported. No additional setup is required until we decide to add a new language.
6+
7+
## Adding a New Language
8+
9+
1. **Register the language** in `settings.py`:
10+
11+
```python
12+
LANGUAGES = [
13+
("en", _("English")),
14+
("es", _("Spanish")), # add new language here
15+
]
16+
```
17+
18+
2. **Generate translation files**:
19+
20+
```bash
21+
python manage.py makemessages -l es
22+
```
23+
24+
This scans all `_()` calls and creates `locale/es/LC_MESSAGES/django.po`.
25+
26+
3. **Translate the strings** in the generated `.po` file. Each entry has a `msgid` which is the original English string and a `msgstr` where the translation goes.
27+
28+
4. **Compile translations**:
29+
30+
```bash
31+
python manage.py compilemessages
32+
```
33+
34+
This produces `.mo` binary files that Django reads at runtime.
35+
36+
37+
## File Structure
38+
39+
```
40+
locale/
41+
└── es/
42+
└── LC_MESSAGES/
43+
├── django.po # human-editable translations
44+
└── django.mo # compiled binary (generated, do not edit)
45+
```
46+
47+
**NOTE:** We should add `*.mo` to `.gitignore` and integrate `compilemessages` as part of our build/deploy process. `makemessages` must be a manual step done by the developer so it can be ready for compilation.
Lines changed: 68 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,164 @@
1+
from django.utils.translation import gettext_lazy as _
2+
3+
14
class docs:
25
# Centralized namespace for all NPD API documentation content
36

47
class constants:
58
# Constant strings that appear throughout content building
69

7-
sort_order_text = "Default sort order: "
10+
sort_order_text = _("Default sort order: ")
811

912
class filters:
1013
# Help text for filter parameters, organized by FHIR resource
1114

1215
class practitioner:
13-
name = (
16+
name = _(
1417
"Filter by practitioner name (first, middle, last, or full name). "
1518
"Name filter accepts websearch syntax."
1619
)
17-
gender = "Filter by practitioner gender"
18-
identifier = (
20+
gender = _("Filter by practitioner gender")
21+
identifier = _(
1922
"Filter by practitioner identifier (NPI or other). Format: value or system|value"
2023
)
21-
type = (
24+
type = _(
2225
"Filter by practitioner type/taxonomy. "
2326
"Practitioner type filter accepts websearch syntax."
2427
)
2528

2629
class organization:
27-
name = "Filter by organization name"
28-
identifier = (
30+
name = _("Filter by organization name")
31+
identifier = _(
2932
"Filter by organization identifier (NPI, EIN, or other). "
3033
"Format: value or system|value"
3134
)
32-
type = "Filter by organization type/taxonomy"
35+
type = _("Filter by organization type/taxonomy")
3336

3437
class location:
35-
name = "Filter by location name"
36-
near = (
38+
name = _("Filter by location name")
39+
near = _(
3740
"Filter by distance from a point expressed as "
3841
"[latitude]|[longitude]|[distance]|[units]. "
3942
"If no units are provided, km is assumed."
4043
)
4144

4245
class endpoint:
43-
name = "Filter by endpoint name"
44-
connection_type = "Filter by endpoint connection type"
45-
payload_type = "Filter by endpoint payload type"
46-
status = "Filter by endpoint status"
46+
name = _("Filter by endpoint name")
47+
connection_type = _("Filter by endpoint connection type")
48+
payload_type = _("Filter by endpoint payload type")
49+
status = _("Filter by endpoint status")
4750

4851
class practitioner_role:
49-
active = "Filter by active status"
50-
role = "Filter by provider role code"
51-
specialty = "Filter by Nucc/Snomed specialty code"
52+
active = _("Filter by active status")
53+
role = _("Filter by provider role code")
54+
specialty = _("Filter by Nucc/Snomed specialty code")
5255

5356
class address:
54-
full = "Filter by any part of address. Address filter accepts websearch syntax."
55-
city = "Filter by city name"
56-
state = "Filter by state (2-letter abbreviation)"
57-
postalcode = "Filter by postal code/zip code"
58-
use = "Filter by address use type"
57+
full = _("Filter by any part of address. Address filter accepts websearch syntax.")
58+
city = _("Filter by city name")
59+
state = _("Filter by state (2-letter abbreviation)")
60+
postalcode = _("Filter by postal code/zip code")
61+
use = _("Filter by address use type")
5962

6063
class endpoints:
6164
# Descriptions for API endpoints
6265

6366
class practitioner:
64-
viewset = "ViewSet for FHIR Practitioner resources"
65-
list_description = (
67+
viewset = _("ViewSet for FHIR Practitioner resources")
68+
list_description = _(
6669
"Query a list of healthcare providers, represented as a "
6770
"bundle of FHIR Practitioner resources"
6871
)
69-
default_sort = "ascending last name, first name"
70-
retrieve_description = "Query a specific provider as a FHIR Practitioner resource"
71-
list_response = (
72+
default_sort = _("ascending last name, first name")
73+
retrieve_description = _("Query a specific provider as a FHIR Practitioner resource")
74+
list_response = _(
7275
"Successfully retrieved FHIR Bundle resource of FHIR Practitioner resources"
7376
)
74-
retrieve_response = "Successfully retrieved FHIR Practitioner resource"
77+
retrieve_response = _("Successfully retrieved FHIR Practitioner resource")
7578

7679
class practitioner_role:
77-
viewset = "ViewSet for FHIR PractitionerRole resources"
78-
list_description = (
80+
viewset = _("ViewSet for FHIR PractitionerRole resources")
81+
list_description = _(
7982
"Query a list of relationships between providers, healthcare "
8083
"organizations, and practice locations, represented as a "
8184
"bundle of FHIR PractitionerRole resources"
8285
)
83-
default_sort = "ascending by location name"
84-
retrieve_description = (
86+
default_sort = _("ascending by location name")
87+
retrieve_description = _(
8588
"Query a specific relationship between providers, healthcare "
8689
"organizations, and practice locations, represented as a "
8790
"FHIR PractitionerRole resource"
8891
)
89-
list_response = (
92+
list_response = _(
9093
"Successfully retrieved FHIR Bundle resource of FHIR PractitionerRole resources"
9194
)
92-
retrieve_response = "Successfully retrieved FHIR PractitionerRole resource"
95+
retrieve_response = _("Successfully retrieved FHIR PractitionerRole resource")
9396

9497
class organization:
95-
viewset = "ViewSet for FHIR Organization resources"
96-
list_description = (
98+
viewset = _("ViewSet for FHIR Organization resources")
99+
list_description = _(
97100
"Query a list of organizations, represented as a bundle "
98101
"of FHIR Organization resources"
99102
)
100-
default_sort = "ascending by organization name"
101-
retrieve_description = (
103+
default_sort = _("ascending by organization name")
104+
retrieve_description = _(
102105
"Query a specific organization, represented as a FHIR Organization resource"
103106
)
104-
list_response = (
107+
list_response = _(
105108
"Successfully retrieved FHIR Bundle resource of FHIR Organization resources"
106109
)
107-
retrieve_response = "Successfully retrieved FHIR Organization resource"
110+
retrieve_response = _("Successfully retrieved FHIR Organization resource")
108111

109112
class organization_affiliation:
110-
viewset = "ViewSet for FHIR EHR Vendor to Organization relationships"
111-
list_description = (
113+
viewset = _("ViewSet for FHIR EHR Vendor to Organization relationships")
114+
list_description = _(
112115
"Query a list of EHR vendor to organization relationships, "
113116
"represented as a bundle of FHIR OrganizationAffiliation "
114117
"resources"
115118
)
116-
default_sort = "ascending by organization name"
117-
retrieve_description = (
119+
default_sort = _("ascending by organization name")
120+
retrieve_description = _(
118121
"Query a specific EHR vendor to organization relationship, "
119122
"represented as a FHIR OrganizationAffiliation resource"
120123
)
121-
list_response = (
124+
list_response = _(
122125
"Successfully retrieved FHIR Bundle resource of "
123126
"FHIR OrganizationAffiliation resources"
124127
)
125-
retrieve_response = "Successfully retrieved FHIR OrganizationAffiliation resource"
128+
retrieve_response = _("Successfully retrieved FHIR OrganizationAffiliation resource")
126129

127130
class location:
128-
viewset = "ViewSet for FHIR Location resources"
129-
list_description = (
131+
viewset = _("ViewSet for FHIR Location resources")
132+
list_description = _(
130133
"Query a list of healthcare practice locations, represented "
131134
"as a bundle of FHIR Location resources"
132135
)
133-
default_sort = "ascending by location name"
134-
retrieve_description = (
136+
default_sort = _("ascending by location name")
137+
retrieve_description = _(
135138
"Query a specific healthcare practice location as a FHIR Location resource"
136139
)
137-
list_response = "Successfully retrieved FHIR Bundle resource of FHIR Location resources"
138-
retrieve_response = "Successfully retrieved FHIR Location resource"
140+
list_response = _(
141+
"Successfully retrieved FHIR Bundle resource of FHIR Location resources"
142+
)
143+
retrieve_response = _("Successfully retrieved FHIR Location resource")
139144

140145
class endpoint:
141-
viewset = "ViewSet for FHIR Endpoint resources"
142-
list_description = (
146+
viewset = _("ViewSet for FHIR Endpoint resources")
147+
list_description = _(
143148
"Query a list of interoperability endpoints, represented "
144149
"as a bundle of FHIR Endpoint resources"
145150
)
146-
default_sort = "ascending by endpoint instance name"
147-
retrieve_description = "Query a specific endpoint as a FHIR Endpoint resource"
148-
list_response = "Successfully retrieved FHIR Bundle resource of FHIR Endpoint resources"
149-
retrieve_response = "Successfully retrieved FHIR Endpoint resource"
151+
default_sort = _("ascending by endpoint instance name")
152+
retrieve_description = _("Query a specific endpoint as a FHIR Endpoint resource")
153+
list_response = _(
154+
"Successfully retrieved FHIR Bundle resource of FHIR Endpoint resources"
155+
)
156+
retrieve_response = _("Successfully retrieved FHIR Endpoint resource")
150157

151158
class capability_statement:
152-
viewset = "ViewSet for FHIR CapabilityStatement resource"
153-
get_description = (
159+
viewset = _("ViewSet for FHIR CapabilityStatement resource")
160+
get_description = _(
154161
"Query metadata about this FHIR instance, represented as "
155162
"FHIR CapabilityStatement resource"
156163
)
157-
get_response = "Successfully retrieved FHIR CapabilityStatement resource"
164+
get_response = _("Successfully retrieved FHIR CapabilityStatement resource")

0 commit comments

Comments
 (0)