@reform-digital/era-group
v1.1.1
Published
ERA Group main website localization code
Readme
Localization Module
Client-side localization for country and language selection with cookie persistence. Used in Webflow projects to drive country/locale dropdowns, URL locale prefixes, contact/social data per office, and optional ConveyThis translation integration.
Overview
- Country & language selection via dropdowns; selection is stored in a cookie and in memory.
- URL locale support: Paths can use a locale prefix (e.g.
/de/,/pt-br/). English can stay at root (no/en). - Persistence: Cookie stores country name/code/slug, language list, selected language, SpendVue flag, and ConveyThis language name.
- Redirects: Switching country can redirect (e.g.
/contact↔/office/{slug}, home, or locale-prefixed URLs). - Contact & social data: Footer company/address and social links are loaded from the appropriate source page (International →
/contact, others →/office/{country-slug}) and applied to targets on the current page. - SpendVue: Optional visibility toggles via
[spend-vue=true]/[spend-vue=false]. - ConveyThis: Optional sync of selected language with the ConveyThis widget.
- Greek: When Greek is active, a CSS custom property is set so headings use the body font (e.g. for missing glyphs).
- First-visit modal: Optional localization modal can show once per browser/session state and be dismissed permanently.
Stored Data (Cookie)
Cookie name: localization. Stored as JSON with:
| Field | Description |
|-------|-------------|
| countryName | Display name (e.g. "International", "United Kingdom") |
| countryCode | Code (e.g. INT, GB) |
| countrySlug | URL slug for country (e.g. united-kingdom) |
| languages | Array of language names for the selected country |
| isSpendVue | true if the selected locale has SpendVue |
| selectedLanguage | Display name of selected language |
| selectedLanguageAbbreviation | BCP-style code (e.g. en, pt-br) |
| selectedLanguageConveyThis | ConveyThis language identifier when using ConveyThis |
DOM Attributes Reference
The script uses data attributes to find and update elements. Add these in Webflow (or your CMS) so the script can bind correctly.
Country / locale
| Selector / Attribute | Purpose |
|----------------------|--------|
| [localization=locale] | One per country/locale option. Wraps the clickable area (e.g. dropdown item). |
| localization-country | On the locale element. Country display name (e.g. International, Austria). |
| localization-country-code | On the locale element. Country code (e.g. INT, AT). |
| [localization=country-code] | Element whose textContent is set to the current country code (e.g. in nav). |
Inside each [localization=locale] you can have:
- A link to the country page, e.g.
<a href="/countries/austria" fs-list-element="item-link">ora[href^="/countries/"]— used to derivecountrySlug. - Language items:
[localization-language]with value = language name (e.g.English,German). - Optional:
[localization-sv=true]to mark this locale as SpendVue. - Optional:
localization-language-abbreviationordata-language-abbreviationon language elements for the BCP code.
Language list (dropdown)
| Selector / Attribute | Purpose |
|----------------------|--------|
| [localization-language=list] | Container for the list of language options (e.g. dropdown list). |
| [localization-language] | Each language option. Attribute value = language name (e.g. English). Skip value list (reserved for the container). |
| [localization=language] | Element whose textContent is set to the current language label (e.g. dropdown toggle text). |
Optional on a language item:
localization-conveythis— ConveyThis language name/code for widget sync.- Inner
divor text — can be used as the display abbreviation (e.g.EN,DE).
First-visit localization modal
| Selector / Attribute | Purpose |
|----------------------|--------|
| [loc-modal-element="modal-loc"] | Modal root. Script shows it with display: flex and hides it with display: none. |
| [loc-modal-element="close-loc"] | Close trigger(s) inside modal. All matching elements are wired (overlay, close button, etc.). |
| [localization=full-country-name] | Heading text target populated with current full country name or International. |
SpendVue visibility
| Selector | Behavior |
|----------|----------|
| [spend-vue=true] | Shown when current country is SpendVue; hidden otherwise. |
| [spend-vue=false] | Hidden when current country is SpendVue; shown otherwise. |
Contact data (footer)
Source content is read from another page and injected into targets on the current page.
| Attribute | Role |
|-----------|------|
| [contact-data=company-source] | On the source page (e.g. /contact or /office/{slug}). Wrapper whose innerHTML is the company block. |
| [contact-data=address-source] | On the source page. Wrapper whose innerHTML is the address block. |
| [contact-data=company-target] | On the current page. Element that receives the company HTML. |
| [contact-data=address-target] | On the current page. Element that receives the address HTML. |
Social links (footer)
| Attribute | Role |
|-----------|------|
| [social-data=linkedin-source] | On the source page. Element (or link inside it) holding the LinkedIn URL. |
| [social-data=x-source] | Same for X (Twitter). |
| [social-data=vimeo-source] | Same for Vimeo. |
| [social-data=linkedin-target] | On the current page. Link to update with LinkedIn href. |
| [social-data=x-target] | Same for X. |
| [social-data=vimeo-target] | Same for Vimeo. |
CMS list reorder and limit
| Selector / Attribute | Purpose |
|----------------------|--------|
| [localization=reorder-list] | List container (e.g. CMS list). Items are reordered so entries matching the current country appear first. |
| localization-limit | Optional, on the same list. Integer. After reorder, only this many items are shown (rest display: none). |
| [fs-list-field="country"] | On each list item. Element(s) whose text is the country name; used to match the current country. |
List items are detected as [role="listitem"] or .w-dyn-item. If the list lives inside a Swiper, the script refreshes the Swiper after reordering.
Form select (country)
Country select fields (e.g. on contact forms) can be synced with the current country. The script looks for elements that match selectors like #country input[type="checkbox"][fs-list-field="country"] and updates selection to match stored country.
URL and locale behavior
- Locale prefix: First path segment matching
aaoraa-bb(two-letter or language-region) is treated as the locale prefix. - Helpers: The script uses internal helpers to get/strip/add locale prefix and to build “country home” paths with the preferred language prefix (e.g.
getCountryHomePath(slug)). English can be normalized to no prefix (e.g./en/→/). - Redirects on country/language change:
- On
/office/{slug}: switching country goes to the new/office/{new-slug}or to/contactfor International. - On
/contact: switching to a non-International country goes to/office/{slug}. - Home and other pages can redirect to the country home or locale-prefixed URL when needed.
- On
- URL-first default order (when stored country is missing/empty): locale-prefix match (/hu/...) -> country-home slug match (/united-states, /hu/hungary) -> International fallback.
- International language pinning: For automatic or manual International selection, language is forced to English (United States) (en).
ConveyThis integration
- Selected language can be synced to the ConveyThis widget. Session state is used to avoid redundant sync.
- Language items can carry
localization-conveythis; the script stores this asselectedLanguageConveyThisand uses it when syncing.
Greek and fonts
When the active language is Greek (by name, abbreviation, or URL locale), the script sets a CSS variable (e.g. --_typography---font-styles--heading) to the body font so headings don’t use a font that lacks Greek glyphs.
Initialization and observers
- DOMContentLoaded: The main entry point runs on
DOMContentLoadedand:- Loads from cookie into
LocalizationData. - Applies URL-locale override when the path has a locale and stored state is International or doesn’t match.
- If no stored data or no country, auto-selects from URL first and then falls back to International with retries.
- Uses URL-first default resolution before International fallback (locale prefix first, then country-home slug).
- Enforces English (United States) when country is International.
- Initializes first-visit localization modal behavior when modal attributes are present.
- Applies stored data to UI (country code, language list filter, SpendVue visibility, Greek font, ConveyThis sync).
- Sets up click delegation for
[localization=locale]and[localization-language], and handlers for locale-aware navigation (home, contact, etc.). - Runs reorder for
[localization=reorder-list], contact/social sync, and country select sync.
- Loads from cookie into
- MutationObserver: A observer watches the nav dropdown container (e.g.
.nav_dropdowns) for child/subtree changes and reapplies stored data when localization elements appear (e.g. after CMS or dynamic UI loads). - Cookie verification: A timer periodically checks that the cookie and in-memory data are in sync and restores the cookie from memory if it was lost.
Event handling (summary)
- Clicks on
[localization=locale]: Extract country/languages/SpendVue from that element, save to cookie and memory, run redirect logic (office/contact/home/locale), then apply UI updates, contact/social sync, reorder, and ConveyThis sync. Dropdown is closed after a short delay. - International locale click behavior: International selection forces language to English (United States) instead of selecting the first visible language item.
- Clicks on language items (inside
[localization-language=list]): Update selected language and abbreviation (and ConveyThis name), persist, optionally redirect to locale-prefixed URL, update language display and ConveyThis. - Home / contact / locale-aware links: Handlers can redirect to the appropriate locale or country path before navigation.
- Modal dismissal behavior: Clicking any [loc-modal-element="close-loc"] or selecting a locale inside the modal dismisses it and stores a one-time dismissal flag.
Dependencies and environment
- Browser: Relies on DOM,
document.cookie,sessionStorage, and optional ConveyThis/Webflow/FinSweet globals. - Webflow: Uses classes like
.w-dropdown,.w-dropdown-toggle,.w-dyn-item,.nav_dropdowns,.nav_dropdown-linkwhere applicable. - FinSweet: Can use
fs-list-element,fs-list-fieldfor CMS-driven links and country fields. Reorder and retry logic account for delayed CMS rendering.
Build and usage
This repo builds the script (e.g. via pnpm build or pnpm prod). The output is intended to be loaded in Webflow (e.g. from the prod folder or via CDN). Include the built script on pages that use the attributes above so that country/language selection, redirects, contact/social sync, and reorder/list limits work as described.
Recent Updates
- Locale-aware navigation: Generic same-origin page links are intercepted and rewritten to the stored locale prefix before navigation (with specialized handlers still taking precedence for home/contact).
- Faster redirect handling: Redirect checks run in capture-phase click handling to reduce delay and avoid intermediate hops caused by other listeners.
- Missing locale fallback on load: If a non-English locale is stored and an unprefixed URL loads (for example
/case-studies), the script can redirect to the localized equivalent (/hu/case-studies). - Contact/office redirect hardening: Contact and office redirects now use stronger language/country fallback resolution to reduce two-step
/office/...then/{locale}/office/...behavior. - Consultants prefilter behavior: Stored non-International country filter is pre-applied on each page visit; users can still clear/change filters during the visit.
- Case studies/insights prefilter behavior: Same as consultants — pre-applied on each visit from stored country, but not force-reapplied after user interaction on that same load.
- URL-first default country selection: First-load default selection now checks locale-prefixed URLs and country-home slugs before any International fallback.
- International language hardening: International selection now always stores/uses English (United States) (en) for both automatic and manual International selection.
- Localization modal support: Added one-time modal support via
loc-modal-element="modal-loc"with multi-element close handling (loc-modal-element="close-loc") and dynamic heading country text ([localization=full-country-name]).
