feat(SW-2873): Move HotelReservationSidePeek to booking-flow * Move sidepeek store to booking-flow * Begin move of HotelReservationSidePeek to booking-flow * Copy Link * Update AccessibilityAccordionItem * Split AccessibilityAccordionItem into two components * Fix tracking for Accordion * Duplicate ButtonLink to booking-flow TEMP * AdditionalAmeneties * wip * Move sidepeek accordion items * Remove temp ButtonLink * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow * Fix accordion tracking * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow * Update exports * Fix self-referencing import * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow * Add 'use client' to tracking function * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow * Fix TEMP folder * Refactor sidepeek tracking * Merge branch 'master' into feat/sw-3218-move-hotelreservationsidepeek-to-booking-flow Approved-by: Joakim Jäderberg
Scandic Monorepo
This is the monorepo for Scandic's web projects.
What's inside?
This repo includes the following packages/apps:
Apps and Packages
scandic-web: Next.js app for our public websitedesign-system: a shared library of styles
Getting Started
To get started, clone this repository and run yarn install in the root directory.
Running scandic-web locally
To run the scandic-web app locally see its README.
Translations
Integration with Lokalise
For more information read about the workflow below.
Message extraction from codebase
Extracts the messages from calls to intl.formatMessage() and other supported methods on intl across our apps and packages.
Running the following command will generate a JSON file at ./scripts/i18n/extracted.json. The format of this file is for consumption by Lokalise. This JSON file is what gets uploaded to Lokalise.
yarn i18n:extract
Checking for changes between codebase and Lokalise
Note
: Diff only considers the English language.
It is recommended to download the latest labels from Lokalise to make sure you have the latest before diffing. See below.
- Run the message extraction above.
- Run
yarn i18n:diff
Message upload to Lokalise
Set the environment variable LOKALISE_API_KEY to the API key for Lokalise in .env.local.
Running the following command will upload the JSON file, that was generated by extraction, to Lokalise.
It supports the different upload phases from Lokalise meaning that once this command completes the messages are available for translation in Lokalise.
yarn i18n:upload
Message download from Lokalise
Set the environment variable LOKALISE_API_KEY to the API key for Lokalise in .env.local.
Running the following command will download the translated assets from Lokalise to your local working copy.
DOCUMENTATION PENDING FOR FULL WORKFLOW.
yarn i18n:download
Message compilation
Compiles the assets that were downloaded from Lokalise into the dictionaries used by the codebase.
DOCUMENTATION PENDING FOR FULL WORKFLOW.
yarn i18n:compile
Message distribution
Distributes the compiled dictionaries to the apps that need them.
yarn i18n:distribute
Convenience script targets
Extract and upload: yarn i18n:push
Download, compile and distribute: yarn i18n:pull
Extract, upload, download and compile (push && pull): yarn i18n:sync
The workflow
We use the following technical stack to handle translations of UI labels.
- react-intl: Library for handling translations in the codebase.
- Lokalise: TMS (Translations Management System) for handling the translations from the editor side.
A translation is usually called a "message" in the context of i18n with react-intl.
In the codebase we use the Imperative API of react-intl. This allows us to use the same patterns and rules regardless of where we are formatting messages (JSX, data, utilities, etc). We do not use the React components of react-intl for the same reason, they would only work in JSX and would possibly differ in implementation and patterns with other parts of the code.
To define messages we primarily invoke intl.formatMessage (but intl has other methods for other purposes too!). We take care not to name the message, we do that by not passing the id attribute to formatMessage. The reason for this is that we also have implemented the @formatjs/cli and the SWC plugin. Due to the SWC plugin being a fairly new project and also due to version mismatching reasons, we are using a pinned version of the SWC plugin. Once we upgrade to Next.js 15 we can upgrade the SWC plugin too and skip pinning it. Together, these two are responsible for allowing us to extract defined messages in our codebase. This optimizes the developer workflow by freeing up developers from having to name things and to not be wary of duplicates/collisions as they will be handled by the extraction tool and Lokalise.
Example of a simple message:
const myMessage = intl.formatMessage({
defaultMessage: "Hello from the docs!",
})
In cases where extra information is helpful to the translators, e.g. short sentences which are hard to translate without context or we are dealing with homographs (words that are spelled the same but have different meanings), we can also specify a description key in the formatMessage call. This allows the tooling to extract all the different permutations of the declared message along with their respective descriptions. The same sentence/word will show up multiple times in Lokalise with different contexts, allowing them to be translated indivudually. The description is intended to assist translators using Lokalise by providing context or additional information. The value is an object with the following structure:
description = string | {
context?: string // natural language string providing context for translators in Lokalise (optional)
limit?: number // character limit for the key enforced by Lokalise (optional)
tags?: string // comma separated string (optional)
}
Examples of a homograph with different context:
const myMessage1 = intl.formatMessage({
defaultMessage: "Book",
description: "The action to reserve a room",
})
const myMessage2 = intl.formatMessage({
defaultMessage: "Book",
description: "A physical book that you can read",
})
Examples with a (contrived) sentence:
const myMessage1 = intl.formatMessage({
defaultMessage: "He gave her a ring!",
description: "A man used a phone to call a woman",
})
const myMessage2 = intl.formatMessage({
defaultMessage: "He gave her a ring!",
description: "A man gave a woman a piece of jewelry",
})
Diagram
A diagram showing the high level workflow of translations. It is currently a manual process syncing messages to and from Lokalise into the codebase.
The following is a diagram showing how all the parts interact with each other. Note that interacting with Lokalise in any way does NOT trigger a build and deploy. A manual action by a developer is required to deploy the latest translations from Lokalise. (Once the manual process reaches maturity we might try and automate it)
Material Symbols
We download the font file from Google Fonts service and host it ourselves.
Configuration
We use the following configuration:
- FILL axis: 0..1
- wght axis: 400
- GRAD axis: 0
- opsz axis: 24
More info at https://developers.google.com/fonts/docs/material_symbols#optimize_the_icon_font
Optimization
We optimize the font size by only including the icons we use in the repository.
Read more at: https://developers.google.com/fonts/docs/material_symbols#use_in_web
Modifying icons
- Update the list of icons to include in
scripts/material-symbols-update.mjs. - Run
yarn run icons:updatein monorepo root.