-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamically change the language of Loader #210
Comments
Similar to #5, it is not possible to currently change the language after the script has been loaded. There is a very old request for if you want to add your particular use case at https://issuetracker.google.com/35819089. I am hoping to address #5 this year. Language would be a potential followup to that. In the meantime, I might investigate #100 to allow reloading the entire API, but that might have billing consequences... Sorry I don't have better news! |
If anyone is looking for a workaround: ConceptWe can actually create a dynamic iframe and load the google maps api in there. Then we can access the api via If we want to destroy the api we can just remove the iframe from the document. This allows us to instantiate the api multiple times with different configurations. In the figure above we are loading the sdk twice (de/en) and then create two maps with different languages that are attached to the same DOM. Code// https://developers.google.com/maps/documentation/javascript/libraries#libraries-for-dynamic-library-import
type Library =
| 'core'
| 'maps'
| 'places'
| 'geocoding'
| 'routes'
| 'marker'
| 'geometry'
| 'elevation'
| 'streetView'
| 'journeySharing'
| 'drawing'
| 'visualization'
type Libraries = Library[]
export type Config = {
/**
* See https://developers.google.com/maps/documentation/javascript/get-api-key.
*/
apiKey: string
/**
* In your application you can specify release channels or version numbers:
*
* The weekly version is specified with `version=weekly`. This version is
* updated once per week, and is the most current.
*
* ```
* const loader = googleMapsApiLoader({apiKey, version: 'weekly'});
* ```
*
* The quarterly version is specified with `version=quarterly`. This version
* is updated once per quarter, and is the most predictable.
*
* ```
* const loader = googleMapsApiLoader({apiKey, version: 'quarterly'});
* ```
*
* The version number is specified with `version=n.nn`. You can choose
* `version=3.40`, `version=3.39`, or `version=3.38`. Version numbers are
* updated once per quarter.
*
* ```
* const loader = googleMapsApiLoader({apiKey, version: '3.40'});
* ```
*
* If you do not explicitly specify a version, you will receive the
* weekly version by default.
*/
version?: string
/**
* When loading the Maps JavaScript API via the URL you may optionally load
* additional libraries through use of the libraries URL parameter. Libraries
* are modules of code that provide additional functionality to the main Maps
* JavaScript API but are not loaded unless you specifically request them.
*
* ```
* const loader = googleMapsApiLoader({
* apiKey,
* libraries: ['drawing', 'geometry', 'places', 'visualization'],
* });
* ```
*
* Set the [list of libraries](https://developers.google.com/maps/documentation/javascript/libraries) for more options.
*/
libraries?: Libraries
/**
* By default, the Maps JavaScript API uses the user's preferred language
* setting as specified in the browser, when displaying textual information
* such as the names for controls, copyright notices, driving directions and
* labels on maps. In most cases, it's preferable to respect the browser
* setting. However, if you want the Maps JavaScript API to ignore the
* browser's language setting, you can force it to display information in a
* particular language when loading the Maps JavaScript API code.
*
* For example, the following example localizes the language to Japan:
*
* ```
* const loader = googleMapsApiLoader({apiKey, language: 'ja', region: 'JP'});
* ```
*
* See the [list of supported
* languages](https://developers.google.com/maps/faq#languagesupport). Note
* that new languages are added often, so this list may not be exhaustive.
*
*/
language?: string
/**
* When you load the Maps JavaScript API from maps.googleapis.com it applies a
* default bias for application behavior towards the United States. If you
* want to alter your application to serve different map tiles or bias the
* application (such as biasing geocoding results towards the region), you can
* override this default behavior by adding a region parameter when loading
* the Maps JavaScript API code.
*
* The region parameter accepts Unicode region subtag identifiers which
* (generally) have a one-to-one mapping to country code Top-Level Domains
* (ccTLDs). Most Unicode region identifiers are identical to ISO 3166-1
* codes, with some notable exceptions. For example, Great Britain's ccTLD is
* "uk" (corresponding to the domain .co.uk) while its region identifier is
* "GB."
*
* For example, the following example localizes the map to the United Kingdom:
*
* ```
* const loader = Loader({apiKey, region: 'GB'});
* ```
*/
region?: string
/**
* Use a custom url and path to load the Google Maps API script.
*/
url?: string
/**
* Use a cryptographic nonce attribute.
*/
nonce?: string
/**
* Maps JS customers can configure HTTP Referrer Restrictions in the Cloud
* Console to limit which URLs are allowed to use a particular API Key. By
* default, these restrictions can be configured to allow only certain paths
* to use an API Key. If any URL on the same domain or origin may use the API
* Key, you can set `auth_referrer_policy=origin` to limit the amount of data
* sent when authorizing requests from the Maps JavaScript API. This is
* available starting in version 3.46. When this parameter is specified and
* HTTP Referrer Restrictions are enabled on Cloud Console, Maps JavaScript
* API will only be able to load if there is an HTTP Referrer Restriction that
* matches the current website's domain without a path specified.
*/
authReferrerPolicy?: 'origin'
/**
* A signal to abort the request
* @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
*/
signal?: AbortSignal
}
/**
* The offical Google Maps API loader is synchronous and pollutes the global scope.
* Therefore it is not possible to change it't language or region after it has been loaded.
* This function creates an isolated iframe and loads the Google Maps API in it.
* This way the global scope is not polluted and the language and region can be changed
* by reloading the iframe.
*
* @url https://github.com/googlemaps/js-api-loader/issues/210
*/
export default function googleMapsApiLoader(
config: Config
): Promise<typeof google> {
return new Promise((resolve, reject) => {
const iframe = document.createElement('iframe')
let isAborted = false
// Handle abort signal
if (config.signal) {
if (config.signal.aborted) {
reject(new DOMException('Aborted', 'AbortError'))
return
}
config.signal.addEventListener('abort', () => {
isAborted = true
if (iframe.parentNode) {
document.body.removeChild(iframe)
}
URL.revokeObjectURL(url)
reject(new DOMException('Aborted', 'AbortError'))
})
}
// Hide the frame
iframe.style.width = '0px'
iframe.style.height = '0px'
iframe.style.border = 'none'
iframe.setAttribute('class', 'google-maps-api-loader2')
// Construct the google sdk URL
const sdkUrl = new URL(
config.url || 'https://maps.googleapis.com/maps/api/js'
)
sdkUrl.searchParams.append('key', config.apiKey)
if (config.version) {
sdkUrl.searchParams.append('v', config.version)
}
if (config.libraries) {
sdkUrl.searchParams.append('libraries', config.libraries.join(','))
}
if (config.language) {
sdkUrl.searchParams.append('language', config.language)
}
if (config.region) {
sdkUrl.searchParams.append('region', config.region)
}
if (config.authReferrerPolicy) {
sdkUrl.searchParams.append(
'auth_referrer_policy',
config.authReferrerPolicy
)
}
// Disable performance warning. This doesn't apply to our
// method since our iframe is loaded asnchronously anyway
sdkUrl.searchParams.append('callback', 'Function.prototype')
// Create the iframe document. The browser will do the rest (head, body etc.)
const html = `<script src="${sdkUrl}"${config.nonce ? ` nonce="${config.nonce}"` : ''}></script>`
const blob = new Blob([html], { type: 'text/html' })
const url = URL.createObjectURL(blob)
iframe.onerror = err => {
if (!isAborted) {
reject(err)
}
}
iframe.onload = () => {
if (isAborted) return
URL.revokeObjectURL(url)
// Grab the google object from the iframe context and resolve the promise
// @ts-expect-error - google is not defined yet
const google = iframe.contentWindow?.google as typeof google | null
if (google) resolve(google)
else reject(new Error('Failed to create isolated Google Map'))
}
iframe.src = url
document.body.appendChild(iframe)
})
} Usageconst abortControllerEN = new AbortController()
const googleEN = await googleMapsApiLoader({
apiKey: '<your_api_key>',
language: 'en',
signal: abortControllerEN.signal
})
// To release the `googleEN` run: `abortControllerEN.abort()`
const abortControllerDE = new AbortController()
const googleDE = await googleMapsApiLoader({
apiKey: '<your_api_key>',
language: 'de',
signal: abortControllerDE.signal
})
// To release the `googleDE` run: `abortControllerDE.abort()` This approach allows us to have multiple maps in different languages in the same page. Caveats
|
Oh wow, that is actually a pretty wild idea. Thanks for sharing! |
Environment details
Steps to reproduce
I load the library as follows
I can dynamically change the language through a dropdown. If i try to reload the library i get an error that says i cannot use the same api key with different params
How can i dynamically change the results of the autocomplete to the desired language?
The text was updated successfully, but these errors were encountered: