@edgekits/astro-i18n 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +23 -16
- package/dist/cli/index.js +4 -0
- package/dist/cli/key-prefix-check.d.ts +6 -0
- package/dist/cli/key-prefix-check.js +27 -0
- package/dist/cli/zone.d.ts +21 -0
- package/dist/cli/zone.js +42 -0
- package/package.json +1 -1
- package/src/cli/index.ts +5 -0
- package/src/cli/key-prefix-check.ts +31 -0
- package/src/cli/zone.ts +64 -0
- package/src/components/LanguageSwitcher.astro +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ All notable changes to this package are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the package uses
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 0.1.8 — 2026-10-18
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `migrate` checks `keyPrefix` against the purge zone before purging. It reads the zone's name (`GET /zones/{id}`, same token) and warns when `keyPrefix` is a hostname with a dot that is neither the zone name nor a subdomain of it (case-insensitive, on a label boundary: `edgekits.dev.evil.com` warns against `edgekits.dev`). The Purge API answers success even for a URL on another proxied zone, so the name is the only thing left to check. The check needs Zone › Zone › Read on the token; without it (or on any lookup failure) `migrate` prints one info line saying the check was skipped. Either way the purge and the `.i18n-hashes.json` write behave as before.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- `LanguageSwitcher`: under `compressHTML` each item rendered two spaces before its label, because a JSX comment among the button's children split the whitespace run. The comment now sits in the `map` callback. 0.1.7's note that the markup "stays the same apart from the item labels" missed this one extra space per item.
|
|
16
|
+
|
|
17
|
+
### Docs
|
|
18
|
+
|
|
19
|
+
- The `keyPrefix` rule corrected (README "Deployment targets", config table, requirements and the skill guide's prerequisites). A unique name without a dot is valid: single-file purge from your zone reaches any URL whose hostname is no proxied Cloudflare zone. The danger is a hostname that is another proxied Cloudflare zone: its URLs can be purged only from that zone, and the API cannot tell you. Zone › Zone › Read on the token is optional and enables the `migrate` check.
|
|
20
|
+
|
|
7
21
|
## 0.1.7 — 2026-10-17
|
|
8
22
|
|
|
9
23
|
### Fixed
|
package/README.md
CHANGED
|
@@ -22,7 +22,8 @@ The design is explained in the edgekits.dev series:
|
|
|
22
22
|
- Node `>=22`.
|
|
23
23
|
- A Workers KV namespace bound as `TRANSLATIONS`.
|
|
24
24
|
- For `migrate` only: the site on a proxied Cloudflare zone, its zone ID, and an API token with
|
|
25
|
-
the Zone › Cache Purge permission.
|
|
25
|
+
the Zone › Cache Purge permission. Zone › Zone › Read is optional; with it, `migrate` checks
|
|
26
|
+
`keyPrefix` against the zone's name.
|
|
26
27
|
|
|
27
28
|
## Quick start
|
|
28
29
|
|
|
@@ -73,18 +74,18 @@ export default defineI18nConfig({
|
|
|
73
74
|
})
|
|
74
75
|
```
|
|
75
76
|
|
|
76
|
-
| Field | Required | Default | Meaning
|
|
77
|
-
| -------------------------- | -------- | ------------ |
|
|
78
|
-
| `languages` | yes | | `{ [locale]: { name, countryCode? } }`; `countryCode` is optional: the code your flag component receives
|
|
79
|
-
| `defaultLocale` | yes | | one of `languages`; its JSON files define the schema
|
|
80
|
-
| `cookie` | yes | | `{ name, maxAge }` (seconds) of the language cookie
|
|
81
|
-
| `keyPrefix` | yes | | KV keys are `<keyPrefix>:<namespace>:<locale>`
|
|
82
|
-
| `missingTranslationBanner` | yes | | enables `MissingTranslationBanner`
|
|
83
|
-
| `routing` | yes | | `'prefix'`
|
|
84
|
-
| `localesDir` | no | `'locales'` | where the CLI reads `<locale>/<namespace>.json`
|
|
85
|
-
| `generatedDir` | no | `'src/i18n'` | where the CLI writes `runtime-constants.ts` and `fallbacks.generated.ts`
|
|
86
|
-
| `countryToLocale` | no | none | geo-IP step of the locale fallback (see Reference)
|
|
87
|
-
| `markdownLocale` | no | none | the locale `.md` endpoints are served in (see Reference)
|
|
77
|
+
| Field | Required | Default | Meaning |
|
|
78
|
+
| -------------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
79
|
+
| `languages` | yes | | `{ [locale]: { name, countryCode? } }`; `countryCode` is optional: the code your flag component receives |
|
|
80
|
+
| `defaultLocale` | yes | | one of `languages`; its JSON files define the schema |
|
|
81
|
+
| `cookie` | yes | | `{ name, maxAge }` (seconds) of the language cookie |
|
|
82
|
+
| `keyPrefix` | yes | | a hostname of your zone, or a unique name without a dot: KV keys are `<keyPrefix>:<namespace>:<locale>` and every cache URL is `https://<keyPrefix>/…` (see [Deployment targets](#deployment-targets)) |
|
|
83
|
+
| `missingTranslationBanner` | yes | | enables `MissingTranslationBanner` |
|
|
84
|
+
| `routing` | yes | | `'prefix'` |
|
|
85
|
+
| `localesDir` | no | `'locales'` | where the CLI reads `<locale>/<namespace>.json` |
|
|
86
|
+
| `generatedDir` | no | `'src/i18n'` | where the CLI writes `runtime-constants.ts` and `fallbacks.generated.ts` |
|
|
87
|
+
| `countryToLocale` | no | none | geo-IP step of the locale fallback (see Reference) |
|
|
88
|
+
| `markdownLocale` | no | none | the locale `.md` endpoints are served in (see Reference) |
|
|
88
89
|
|
|
89
90
|
Locale codes (the keys of `languages`) are lowercase everywhere: the config, the `locales/`
|
|
90
91
|
folders and the URLs (`pt-br`, not `pt-BR`). `toIetfTag()` from `./format` gives the IETF form
|
|
@@ -408,9 +409,13 @@ a regular Worker: `request.cf`, `caches.default` and `env` behave as usual, and
|
|
|
408
409
|
language cookie and the relative redirects keep visitors on their custom hostname. Two
|
|
409
410
|
constraints:
|
|
410
411
|
|
|
411
|
-
- `keyPrefix`
|
|
412
|
-
|
|
413
|
-
|
|
412
|
+
- `keyPrefix` is the host of every cache URL that `migrate` purges from the zone
|
|
413
|
+
`CLOUDFLARE_ZONE_ID` names. Recommended: a hostname of that zone (the zone name or a
|
|
414
|
+
subdomain). A unique name without a dot works too: single-file purge from your zone reaches
|
|
415
|
+
any URL whose hostname is no proxied Cloudflare zone. Never another domain that is a proxied
|
|
416
|
+
Cloudflare zone: its URLs can be purged only from that zone, by its owner, and the Purge API
|
|
417
|
+
reports success anyway. When the token can read the zone (Zone › Zone › Read), `migrate`
|
|
418
|
+
warns about a dotted `keyPrefix` outside the zone.
|
|
414
419
|
- One tenant per build. `keyPrefix`, the locales, `defaultLocale` and `countryToLocale` are
|
|
415
420
|
build-time constants, so one Worker serving many custom hostnames serves the same translation
|
|
416
421
|
set to all of them.
|
|
@@ -521,6 +526,8 @@ npx skills add EdgeKits/astro-i18n --skill astro-i18n
|
|
|
521
526
|
- A Cloudflare account. Local development needs none until you create the real KV namespace.
|
|
522
527
|
- For `edgekits-i18n migrate` (pushing copy to production): the site on a proxied Cloudflare
|
|
523
528
|
zone (orange cloud), that zone's ID, and an API token with the Zone › Cache Purge permission.
|
|
529
|
+
Zone › Zone › Read on the same token is optional: with it, `migrate` checks `keyPrefix`
|
|
530
|
+
against the zone's name.
|
|
524
531
|
|
|
525
532
|
### First run on a fresh site
|
|
526
533
|
|
package/dist/cli/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { diffHashes, purgeUrlsFor } from "./hashes.js";
|
|
|
8
8
|
import { CONFIG_FILE, loadI18nConfig } from "./load-config.js";
|
|
9
9
|
import { purgeTranslationsCache } from "./purge.js";
|
|
10
10
|
import { getKvNamespaceId, getWranglerVar, KV_BINDING, resolveWranglerBin } from "./wrangler.js";
|
|
11
|
+
import { checkKeyPrefixZone } from "./zone.js";
|
|
11
12
|
const HELP = `Usage: edgekits-i18n <command> [--fallbacks]
|
|
12
13
|
|
|
13
14
|
Reads ${CONFIG_FILE} and <localesDir>/<locale>/<namespace>.json from the current directory.
|
|
@@ -256,6 +257,9 @@ async function deploy({ config, kvId, wranglerBin, isLocal, changedKeys, current
|
|
|
256
257
|
'Skipping cache purge — hash file NOT updated. Re-run `edgekits-i18n migrate` after setting the secret.');
|
|
257
258
|
}
|
|
258
259
|
else {
|
|
260
|
+
// Advisory: the Purge API reports success even for URLs it cannot reach, so the
|
|
261
|
+
// name is all there is to check. Never decides shouldWriteHashes.
|
|
262
|
+
await checkKeyPrefixZone(config.keyPrefix, zoneId, apiToken);
|
|
259
263
|
console.log(`[i18n] Purging ${purgeUrls.length} cache entries via Cloudflare API...`);
|
|
260
264
|
const purgeSuccess = await purgeTranslationsCache(zoneId, apiToken, purgeUrls);
|
|
261
265
|
if (purgeSuccess) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The warning `migrate` prints when `keyPrefix` is a dotted hostname outside `zoneName`,
|
|
3
|
+
* or null when it is dotless, the zone name itself, or a subdomain of it (label
|
|
4
|
+
* boundary, case-insensitive, both sides trimmed). Pure: no network, no `process`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function keyPrefixZoneWarning(keyPrefix: string, zoneName: string): string | null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// keyPrefix against the purge zone
|
|
3
|
+
//
|
|
4
|
+
// Every cache URL is https://<keyPrefix>/..., and `migrate` purges those URLs from the
|
|
5
|
+
// zone CLOUDFLARE_ZONE_ID names. A URL a Worker cached is purged from the Worker's own
|
|
6
|
+
// zone when its hostname is no Cloudflare zone or a non-proxied one. A hostname that is
|
|
7
|
+
// an active proxied Cloudflare zone can be purged only from that zone, by its owner, and
|
|
8
|
+
// the Purge API answers `success: true` either way. The response cannot show the miss,
|
|
9
|
+
// so the only check left is the name. A proxied zone always has a dot: a dotless
|
|
10
|
+
// keyPrefix never collides with one.
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* The warning `migrate` prints when `keyPrefix` is a dotted hostname outside `zoneName`,
|
|
14
|
+
* or null when it is dotless, the zone name itself, or a subdomain of it (label
|
|
15
|
+
* boundary, case-insensitive, both sides trimmed). Pure: no network, no `process`.
|
|
16
|
+
*/
|
|
17
|
+
export function keyPrefixZoneWarning(keyPrefix, zoneName) {
|
|
18
|
+
const host = keyPrefix.trim().toLowerCase();
|
|
19
|
+
const zone = zoneName.trim().toLowerCase();
|
|
20
|
+
if (!host.includes('.') || host === zone || host.endsWith(`.${zone}`))
|
|
21
|
+
return null;
|
|
22
|
+
return (`[i18n] ⚠️ keyPrefix "${keyPrefix.trim()}" is a hostname outside the zone the purge is issued against (${zoneName.trim()}).\n` +
|
|
23
|
+
' The Purge API answers success either way, so this run cannot tell whether the edge cache was purged.\n' +
|
|
24
|
+
` It is harmless only if "${keyPrefix.trim()}" is no proxied Cloudflare zone: a URL on a proxied zone\n` +
|
|
25
|
+
' can be purged only from that zone, by its owner. Fix: a keyPrefix inside the zone (its name or a\n' +
|
|
26
|
+
' subdomain), or a unique name without a dot.');
|
|
27
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type ZoneDeps = {
|
|
2
|
+
fetch?: (input: string, init: RequestInit) => Promise<Response>;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Read the zone's name: `GET /zones/{zoneId}`, `result.name`. Needs Zone › Zone › Read
|
|
6
|
+
* on the token. Never throws: any failure (non-2xx, network error, unexpected body)
|
|
7
|
+
* comes back as `{ error }`.
|
|
8
|
+
*
|
|
9
|
+
* `deps` replaces `fetch` in tests.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readZoneName(zoneId: string, apiToken: string, deps?: ZoneDeps): Promise<{
|
|
12
|
+
name: string;
|
|
13
|
+
} | {
|
|
14
|
+
error: string;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Print the keyPrefix warning when `keyPrefix` lies outside the zone, or one info line
|
|
18
|
+
* when the zone name cannot be read. Advisory only: it returns nothing, and the purge
|
|
19
|
+
* runs the same either way.
|
|
20
|
+
*/
|
|
21
|
+
export declare function checkKeyPrefixZone(keyPrefix: string, zoneId: string, apiToken: string, deps?: ZoneDeps): Promise<void>;
|
package/dist/cli/zone.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { keyPrefixZoneWarning } from "./key-prefix-check.js";
|
|
2
|
+
/**
|
|
3
|
+
* Read the zone's name: `GET /zones/{zoneId}`, `result.name`. Needs Zone › Zone › Read
|
|
4
|
+
* on the token. Never throws: any failure (non-2xx, network error, unexpected body)
|
|
5
|
+
* comes back as `{ error }`.
|
|
6
|
+
*
|
|
7
|
+
* `deps` replaces `fetch` in tests.
|
|
8
|
+
*/
|
|
9
|
+
export async function readZoneName(zoneId, apiToken, deps = {}) {
|
|
10
|
+
const { fetch: fetchFn = fetch } = deps;
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetchFn(`https://api.cloudflare.com/client/v4/zones/${zoneId}`, {
|
|
13
|
+
method: 'GET',
|
|
14
|
+
headers: { Authorization: `Bearer ${apiToken}` },
|
|
15
|
+
});
|
|
16
|
+
if (!response.ok)
|
|
17
|
+
return { error: `HTTP ${response.status}` };
|
|
18
|
+
const body = (await response.json());
|
|
19
|
+
const name = body?.result?.name;
|
|
20
|
+
if (typeof name !== 'string' || name.trim() === '')
|
|
21
|
+
return { error: 'no result.name in the response' };
|
|
22
|
+
return { name };
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Print the keyPrefix warning when `keyPrefix` lies outside the zone, or one info line
|
|
30
|
+
* when the zone name cannot be read. Advisory only: it returns nothing, and the purge
|
|
31
|
+
* runs the same either way.
|
|
32
|
+
*/
|
|
33
|
+
export async function checkKeyPrefixZone(keyPrefix, zoneId, apiToken, deps = {}) {
|
|
34
|
+
const zone = await readZoneName(zoneId, apiToken, deps);
|
|
35
|
+
if ('error' in zone) {
|
|
36
|
+
console.log(`[i18n] ℹ️ Could not read the zone name (${zone.error}), so the keyPrefix check was skipped (needs Zone › Zone › Read on the token).`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const warning = keyPrefixZoneWarning(keyPrefix, zone.name);
|
|
40
|
+
if (warning)
|
|
41
|
+
console.warn(warning);
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgekits/astro-i18n",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Edge-native i18n for Astro on Cloudflare Workers: translations in Workers KV, a per-namespace edge cache invalidated by the Purge API, and typed keys.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
package/src/cli/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { diffHashes, purgeUrlsFor, type HashMap } from './hashes.ts'
|
|
|
19
19
|
import { CONFIG_FILE, loadI18nConfig } from './load-config.ts'
|
|
20
20
|
import { purgeTranslationsCache } from './purge.ts'
|
|
21
21
|
import { getKvNamespaceId, getWranglerVar, KV_BINDING, resolveWranglerBin } from './wrangler.ts'
|
|
22
|
+
import { checkKeyPrefixZone } from './zone.ts'
|
|
22
23
|
|
|
23
24
|
const HELP = `Usage: edgekits-i18n <command> [--fallbacks]
|
|
24
25
|
|
|
@@ -346,6 +347,10 @@ async function deploy({
|
|
|
346
347
|
'Skipping cache purge — hash file NOT updated. Re-run `edgekits-i18n migrate` after setting the secret.',
|
|
347
348
|
)
|
|
348
349
|
} else {
|
|
350
|
+
// Advisory: the Purge API reports success even for URLs it cannot reach, so the
|
|
351
|
+
// name is all there is to check. Never decides shouldWriteHashes.
|
|
352
|
+
await checkKeyPrefixZone(config.keyPrefix, zoneId, apiToken)
|
|
353
|
+
|
|
349
354
|
console.log(`[i18n] Purging ${purgeUrls.length} cache entries via Cloudflare API...`)
|
|
350
355
|
const purgeSuccess = await purgeTranslationsCache(zoneId, apiToken, purgeUrls)
|
|
351
356
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// keyPrefix against the purge zone
|
|
3
|
+
//
|
|
4
|
+
// Every cache URL is https://<keyPrefix>/..., and `migrate` purges those URLs from the
|
|
5
|
+
// zone CLOUDFLARE_ZONE_ID names. A URL a Worker cached is purged from the Worker's own
|
|
6
|
+
// zone when its hostname is no Cloudflare zone or a non-proxied one. A hostname that is
|
|
7
|
+
// an active proxied Cloudflare zone can be purged only from that zone, by its owner, and
|
|
8
|
+
// the Purge API answers `success: true` either way. The response cannot show the miss,
|
|
9
|
+
// so the only check left is the name. A proxied zone always has a dot: a dotless
|
|
10
|
+
// keyPrefix never collides with one.
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The warning `migrate` prints when `keyPrefix` is a dotted hostname outside `zoneName`,
|
|
15
|
+
* or null when it is dotless, the zone name itself, or a subdomain of it (label
|
|
16
|
+
* boundary, case-insensitive, both sides trimmed). Pure: no network, no `process`.
|
|
17
|
+
*/
|
|
18
|
+
export function keyPrefixZoneWarning(keyPrefix: string, zoneName: string): string | null {
|
|
19
|
+
const host = keyPrefix.trim().toLowerCase()
|
|
20
|
+
const zone = zoneName.trim().toLowerCase()
|
|
21
|
+
|
|
22
|
+
if (!host.includes('.') || host === zone || host.endsWith(`.${zone}`)) return null
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
`[i18n] ⚠️ keyPrefix "${keyPrefix.trim()}" is a hostname outside the zone the purge is issued against (${zoneName.trim()}).\n` +
|
|
26
|
+
' The Purge API answers success either way, so this run cannot tell whether the edge cache was purged.\n' +
|
|
27
|
+
` It is harmless only if "${keyPrefix.trim()}" is no proxied Cloudflare zone: a URL on a proxied zone\n` +
|
|
28
|
+
' can be purged only from that zone, by its owner. Fix: a keyPrefix inside the zone (its name or a\n' +
|
|
29
|
+
' subdomain), or a unique name without a dot.'
|
|
30
|
+
)
|
|
31
|
+
}
|
package/src/cli/zone.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { keyPrefixZoneWarning } from './key-prefix-check.ts'
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Zone name lookup for the keyPrefix check (migrate only)
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
export type ZoneDeps = {
|
|
8
|
+
fetch?: (input: string, init: RequestInit) => Promise<Response>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Read the zone's name: `GET /zones/{zoneId}`, `result.name`. Needs Zone › Zone › Read
|
|
13
|
+
* on the token. Never throws: any failure (non-2xx, network error, unexpected body)
|
|
14
|
+
* comes back as `{ error }`.
|
|
15
|
+
*
|
|
16
|
+
* `deps` replaces `fetch` in tests.
|
|
17
|
+
*/
|
|
18
|
+
export async function readZoneName(
|
|
19
|
+
zoneId: string,
|
|
20
|
+
apiToken: string,
|
|
21
|
+
deps: ZoneDeps = {},
|
|
22
|
+
): Promise<{ name: string } | { error: string }> {
|
|
23
|
+
const { fetch: fetchFn = fetch } = deps
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const response = await fetchFn(`https://api.cloudflare.com/client/v4/zones/${zoneId}`, {
|
|
27
|
+
method: 'GET',
|
|
28
|
+
headers: { Authorization: `Bearer ${apiToken}` },
|
|
29
|
+
})
|
|
30
|
+
if (!response.ok) return { error: `HTTP ${response.status}` }
|
|
31
|
+
|
|
32
|
+
const body = (await response.json()) as { result?: { name?: unknown } } | null
|
|
33
|
+
const name = body?.result?.name
|
|
34
|
+
if (typeof name !== 'string' || name.trim() === '') return { error: 'no result.name in the response' }
|
|
35
|
+
|
|
36
|
+
return { name }
|
|
37
|
+
} catch (err) {
|
|
38
|
+
return { error: err instanceof Error ? err.message : String(err) }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Print the keyPrefix warning when `keyPrefix` lies outside the zone, or one info line
|
|
44
|
+
* when the zone name cannot be read. Advisory only: it returns nothing, and the purge
|
|
45
|
+
* runs the same either way.
|
|
46
|
+
*/
|
|
47
|
+
export async function checkKeyPrefixZone(
|
|
48
|
+
keyPrefix: string,
|
|
49
|
+
zoneId: string,
|
|
50
|
+
apiToken: string,
|
|
51
|
+
deps: ZoneDeps = {},
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
const zone = await readZoneName(zoneId, apiToken, deps)
|
|
54
|
+
|
|
55
|
+
if ('error' in zone) {
|
|
56
|
+
console.log(
|
|
57
|
+
`[i18n] ℹ️ Could not read the zone name (${zone.error}), so the keyPrefix check was skipped (needs Zone › Zone › Read on the token).`,
|
|
58
|
+
)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const warning = keyPrefixZoneWarning(keyPrefix, zone.name)
|
|
63
|
+
if (warning) console.warn(warning)
|
|
64
|
+
}
|
|
@@ -69,6 +69,9 @@ const { uiLocale: currentLang } = Astro.locals
|
|
|
69
69
|
const label = LANGUAGES[locale]
|
|
70
70
|
const flagCode = label.countryCode
|
|
71
71
|
|
|
72
|
+
// The label is the language's own name: `lang` lets a screen reader say it in that
|
|
73
|
+
// language. (A comment here, not among the button's children: there it splits the
|
|
74
|
+
// whitespace run, and compressHTML keeps two spaces before the label.)
|
|
72
75
|
return (
|
|
73
76
|
<button
|
|
74
77
|
type="button"
|
|
@@ -79,7 +82,6 @@ const { uiLocale: currentLang } = Astro.locals
|
|
|
79
82
|
role="menuitem"
|
|
80
83
|
disabled={isActive}
|
|
81
84
|
>
|
|
82
|
-
{/* The label is the language's own name: `lang` lets a screen reader say it in that language. */}
|
|
83
85
|
<span class={classes.itemLabel} lang={locale}>
|
|
84
86
|
{Flag && flagCode && <Flag code={flagCode} size={16} />}
|
|
85
87
|
{label.name}
|