refactor: migrate legacy wire namespace to wrn
This commit is contained in:
1 parent
ec23dd4c93
commit
2c960fc1dc
488 files changed
+27306
-19063
No files matched your search
@@ -22,7 +22,7 @@ const i18n = resolveI18n(loadLocales("app/locales", { strict: true }), {
|
||||
default: "en",
|
||||
locales: ["en", "mr", "hi"],
|
||||
fallbacks: { "mr-IN": ["mr", "en"] },
|
||||
cookie: { name: "wire-lang", sameSite: "Lax", secure: true },
|
||||
cookie: { name: "wrn-lang", sameSite: "Lax", secure: true },
|
||||
});
|
||||
|
||||
const lang = resolveLang(i18n, cookieValue, request.headers.get("accept-language"));
|
||||
@@ -57,7 +57,7 @@ Enable `i18nPlugin()` to use:
|
||||
- `<LanguageSwitcher />`
|
||||
- `<LocaleStatus />`
|
||||
|
||||
`LanguageSwitcher` renders a native `select[data-wire-lang]`. The packaged runtime validates the
|
||||
`LanguageSwitcher` renders a native `select[data-wrn-lang]`. The packaged runtime validates the
|
||||
selection against the configured locales, writes the configured language cookie, updates the
|
||||
document `lang`/`dir` attributes, emits `wrnexus:language-change`, and reloads so the next SSR
|
||||
request uses the same cookie. No application-owned browser script is required.
|
||||
|
||||
@@ -30,76 +30,76 @@ component LanguageSwitcher {
|
||||
view {
|
||||
<div
|
||||
{...attrs}
|
||||
class='wire-language-switcher {fullWidth ? "wire-language-switcher--full" : ""} {responsive ? "wire-language-switcher--responsive" : ""} {compact || variant == "compact" ? "wire-language-switcher--compact" : ""} {class}'
|
||||
class='wrn-language-switcher {fullWidth ? "wrn-language-switcher--full" : ""} {responsive ? "wrn-language-switcher--responsive" : ""} {compact || variant == "compact" ? "wrn-language-switcher--compact" : ""} {class}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
data-variant='{variant}'
|
||||
data-current-language='{current}'
|
||||
>
|
||||
{#if variant == "segmented"}
|
||||
<fieldset class="wire-language-switcher__fieldset">
|
||||
{#if showLabel}<legend class="wire-language-switcher__label">{label}</legend>{/if}
|
||||
<div class="wire-language-switcher__segments" role="group" aria-label='{label}'>
|
||||
<fieldset class="wrn-language-switcher__fieldset">
|
||||
{#if showLabel}<legend class="wrn-language-switcher__label">{label}</legend>{/if}
|
||||
<div class="wrn-language-switcher__segments" role="group" aria-label='{label}'>
|
||||
{#each locales as locale}
|
||||
<button
|
||||
type="button"
|
||||
class="wire-language-switcher__segment"
|
||||
data-wire-lang-set='{locale.value}'
|
||||
class="wrn-language-switcher__segment"
|
||||
data-wrn-lang-set='{locale.value}'
|
||||
aria-pressed='{locale.value == current}'
|
||||
disabled='{locale.disabled}'
|
||||
@click='selected(locale.value, event)'
|
||||
><span class="wire-language-switcher__long-label">{locale.label}</span><span class="wire-language-switcher__short-label">{locale.shortLabel}</span></button>
|
||||
><span class="wrn-language-switcher__long-label">{locale.label}</span><span class="wrn-language-switcher__short-label">{locale.shortLabel}</span></button>
|
||||
{/each}
|
||||
</div>
|
||||
{#if showHelper && helperText}<span class="wire-language-switcher__helper">{helperText}</span>{/if}
|
||||
{#if showHelper && helperText}<span class="wrn-language-switcher__helper">{helperText}</span>{/if}
|
||||
</fieldset>
|
||||
{/if}
|
||||
{#if variant != "segmented"}
|
||||
<label class="wire-language-switcher__field">
|
||||
{#if showLabel}<span class="wire-language-switcher__label">{label}</span>{/if}
|
||||
<span class="wire-language-switcher__control">
|
||||
{#if icon}<span class='{icon} wire-language-switcher__icon' aria-hidden="true"></span>{/if}
|
||||
<label class="wrn-language-switcher__field">
|
||||
{#if showLabel}<span class="wrn-language-switcher__label">{label}</span>{/if}
|
||||
<span class="wrn-language-switcher__control">
|
||||
{#if icon}<span class='{icon} wrn-language-switcher__icon' aria-hidden="true"></span>{/if}
|
||||
<select
|
||||
data-wire-lang
|
||||
data-wrn-lang
|
||||
name="language"
|
||||
aria-label='{label}'
|
||||
class="wire-language-switcher__select"
|
||||
class="wrn-language-switcher__select"
|
||||
@change='changed(event)'
|
||||
>
|
||||
{#if placeholder}<option value="" disabled selected='{current == ""}'>{placeholder}</option>{/if}
|
||||
{#each locales as locale}<option value='{locale.value}' selected='{locale.value == current}' disabled='{locale.disabled}' data-wire-lang-option data-label-long='{locale.label}' data-label-short='{locale.shortLabel}'>{locale.label}</option>{/each}
|
||||
{#each locales as locale}<option value='{locale.value}' selected='{locale.value == current}' disabled='{locale.disabled}' data-wrn-lang-option data-label-long='{locale.label}' data-label-short='{locale.shortLabel}'>{locale.label}</option>{/each}
|
||||
</select>
|
||||
<span class="icon-[lucide--chevron-down] wire-language-switcher__chevron" aria-hidden="true"></span>
|
||||
<span class="icon-[lucide--chevron-down] wrn-language-switcher__chevron" aria-hidden="true"></span>
|
||||
</span>
|
||||
{#if showHelper && helperText}<span class="wire-language-switcher__helper">{helperText}</span>{/if}
|
||||
{#if showHelper && helperText}<span class="wrn-language-switcher__helper">{helperText}</span>{/if}
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-language-switcher { min-width: 13rem; color: var(--wire-color-text); }
|
||||
.wire-language-switcher--full, .wire-language-switcher--full .wire-language-switcher__field { width: 100%; }
|
||||
.wire-language-switcher__field, .wire-language-switcher__fieldset { display: flex; margin: 0; padding: 0; border: 0; flex-direction: column; gap: .4rem; }
|
||||
.wire-language-switcher__label { padding: 0; font-size: .8125rem; line-height: 1.2; font-weight: 650; color: var(--wire-color-text); }
|
||||
.wire-language-switcher__control { position: relative; display: flex; align-items: center; }
|
||||
.wire-language-switcher__select { width: 100%; min-height: 2.75rem; appearance: none; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius-lg); background: var(--wire-color-surface); padding: .65rem 2.5rem .65rem 2.35rem; color: var(--wire-color-text); font: inherit; font-size: .875rem; font-weight: 550; box-shadow: 0 1px 2px color-mix(in srgb, var(--wire-color-text) 7%, transparent); outline: none; transition: border-color .16s ease, box-shadow .16s ease, background .16s ease; }
|
||||
.wire-language-switcher__select:hover { border-color: color-mix(in srgb, var(--wire-color-primary) 45%, var(--wire-color-border)); }
|
||||
.wire-language-switcher__select:focus-visible { border-color: var(--wire-color-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-color-primary) 18%, transparent); }
|
||||
.wire-language-switcher__icon, .wire-language-switcher__chevron { position: absolute; z-index: 1; width: 1rem; height: 1rem; pointer-events: none; color: var(--wire-color-muted); }
|
||||
.wire-language-switcher__icon { left: .8rem; }
|
||||
.wire-language-switcher__chevron { right: .8rem; }
|
||||
.wire-language-switcher__helper { font-size: .72rem; line-height: 1.35; color: var(--wire-color-muted); }
|
||||
.wire-language-switcher--compact { min-width: 0; }
|
||||
.wire-language-switcher--compact .wire-language-switcher__label, .wire-language-switcher--compact .wire-language-switcher__helper { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
|
||||
.wire-language-switcher--compact .wire-language-switcher__select { min-height: 2.25rem; border-radius: 999px; padding-top: .4rem; padding-bottom: .4rem; }
|
||||
.wire-language-switcher__segments { display: inline-flex; flex-wrap: wrap; gap: .25rem; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius-xl); background: var(--wire-color-surface-2); padding: .25rem; }
|
||||
.wire-language-switcher__segment { min-height: 2.25rem; border: 0; border-radius: var(--wire-radius-lg); background: transparent; padding: .45rem .8rem; color: var(--wire-color-muted); font: inherit; font-size: .8125rem; font-weight: 650; cursor: pointer; transition: color .16s ease, background .16s ease, box-shadow .16s ease; }
|
||||
.wire-language-switcher__segment:hover { color: var(--wire-color-text); }
|
||||
.wire-language-switcher__segment[aria-pressed="true"] { background: var(--wire-color-surface); color: var(--wire-color-primary); box-shadow: 0 1px 3px color-mix(in srgb, var(--wire-color-text) 12%, transparent); }
|
||||
.wire-language-switcher__segment:focus-visible { outline: 2px solid var(--wire-color-primary); outline-offset: 2px; }
|
||||
.wire-language-switcher__short-label { display: none; }
|
||||
@media (max-width: 640px) { .wire-language-switcher--responsive { width: 100%; min-width: 0; } .wire-language-switcher--responsive .wire-language-switcher__segments { display: grid; width: 100%; grid-template-columns: repeat(auto-fit, minmax(3.5rem, 1fr)); } .wire-language-switcher--responsive .wire-language-switcher__long-label { display: none; } .wire-language-switcher--responsive .wire-language-switcher__short-label { display: inline; } }
|
||||
@media (prefers-reduced-motion: reduce) { .wire-language-switcher__select, .wire-language-switcher__segment { transition: none; } }
|
||||
.wrn-language-switcher { min-width: 13rem; color: var(--wrn-color-text); }
|
||||
.wrn-language-switcher--full, .wrn-language-switcher--full .wrn-language-switcher__field { width: 100%; }
|
||||
.wrn-language-switcher__field, .wrn-language-switcher__fieldset { display: flex; margin: 0; padding: 0; border: 0; flex-direction: column; gap: .4rem; }
|
||||
.wrn-language-switcher__label { padding: 0; font-size: .8125rem; line-height: 1.2; font-weight: 650; color: var(--wrn-color-text); }
|
||||
.wrn-language-switcher__control { position: relative; display: flex; align-items: center; }
|
||||
.wrn-language-switcher__select { width: 100%; min-height: 2.75rem; appearance: none; border: 1px solid var(--wrn-color-border); border-radius: var(--wrn-radius-lg); background: var(--wrn-color-surface); padding: .65rem 2.5rem .65rem 2.35rem; color: var(--wrn-color-text); font: inherit; font-size: .875rem; font-weight: 550; box-shadow: 0 1px 2px color-mix(in srgb, var(--wrn-color-text) 7%, transparent); outline: none; transition: border-color .16s ease, box-shadow .16s ease, background .16s ease; }
|
||||
.wrn-language-switcher__select:hover { border-color: color-mix(in srgb, var(--wrn-color-primary) 45%, var(--wrn-color-border)); }
|
||||
.wrn-language-switcher__select:focus-visible { border-color: var(--wrn-color-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-primary) 18%, transparent); }
|
||||
.wrn-language-switcher__icon, .wrn-language-switcher__chevron { position: absolute; z-index: 1; width: 1rem; height: 1rem; pointer-events: none; color: var(--wrn-color-muted); }
|
||||
.wrn-language-switcher__icon { left: .8rem; }
|
||||
.wrn-language-switcher__chevron { right: .8rem; }
|
||||
.wrn-language-switcher__helper { font-size: .72rem; line-height: 1.35; color: var(--wrn-color-muted); }
|
||||
.wrn-language-switcher--compact { min-width: 0; }
|
||||
.wrn-language-switcher--compact .wrn-language-switcher__label, .wrn-language-switcher--compact .wrn-language-switcher__helper { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
|
||||
.wrn-language-switcher--compact .wrn-language-switcher__select { min-height: 2.25rem; border-radius: 999px; padding-top: .4rem; padding-bottom: .4rem; }
|
||||
.wrn-language-switcher__segments { display: inline-flex; flex-wrap: wrap; gap: .25rem; border: 1px solid var(--wrn-color-border); border-radius: var(--wrn-radius-xl); background: var(--wrn-color-surface-2); padding: .25rem; }
|
||||
.wrn-language-switcher__segment { min-height: 2.25rem; border: 0; border-radius: var(--wrn-radius-lg); background: transparent; padding: .45rem .8rem; color: var(--wrn-color-muted); font: inherit; font-size: .8125rem; font-weight: 650; cursor: pointer; transition: color .16s ease, background .16s ease, box-shadow .16s ease; }
|
||||
.wrn-language-switcher__segment:hover { color: var(--wrn-color-text); }
|
||||
.wrn-language-switcher__segment[aria-pressed="true"] { background: var(--wrn-color-surface); color: var(--wrn-color-primary); box-shadow: 0 1px 3px color-mix(in srgb, var(--wrn-color-text) 12%, transparent); }
|
||||
.wrn-language-switcher__segment:focus-visible { outline: 2px solid var(--wrn-color-primary); outline-offset: 2px; }
|
||||
.wrn-language-switcher__short-label { display: none; }
|
||||
@media (max-width: 640px) { .wrn-language-switcher--responsive { width: 100%; min-width: 0; } .wrn-language-switcher--responsive .wrn-language-switcher__segments { display: grid; width: 100%; grid-template-columns: repeat(auto-fit, minmax(3.5rem, 1fr)); } .wrn-language-switcher--responsive .wrn-language-switcher__long-label { display: none; } .wrn-language-switcher--responsive .wrn-language-switcher__short-label { display: inline; } }
|
||||
@media (prefers-reduced-motion: reduce) { .wrn-language-switcher__select, .wrn-language-switcher__segment { transition: none; } }
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ component LocaleStatus {
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<Badge label='{locale}' color='{color}' variant="soft" size='{size}' />
|
||||
<Badge label='{direction == "rtl" ? "RTL" : "LTR"}' color="neutral" variant="outline" size='{size}' />
|
||||
{#if total > 0}<span class="text-sm text-[var(--wire-color-muted)]">{translated}/{total} translations</span>{/if}
|
||||
{#if total > 0}<span class="text-sm text-[var(--wrn-color-muted)]">{translated}/{total} translations</span>{/if}
|
||||
</div>
|
||||
</Card>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.8.8",
|
||||
"version": "0.8.9",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
+15
-15
@@ -58,7 +58,7 @@ export interface ResolvedI18n {
|
||||
cookie: Required<I18nCookieConfig>;
|
||||
}
|
||||
|
||||
export const LANG_COOKIE = "wire-lang";
|
||||
export const LANG_COOKIE = "wrn-lang";
|
||||
export const I18N_JS_HREF = "/__wrnexus/i18n.js";
|
||||
|
||||
const UNSAFE_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
||||
@@ -425,7 +425,7 @@ function safeJson(value: unknown): string {
|
||||
|
||||
export function renderI18nData(i18n: ResolvedI18n, lang: string): string {
|
||||
const active = i18n.langs.includes(lang) ? lang : i18n.default;
|
||||
return `window.__wireI18n=${safeJson({
|
||||
return `window.__wrnI18n=${safeJson({
|
||||
lang: active,
|
||||
langs: i18n.langs,
|
||||
default: i18n.default,
|
||||
@@ -455,7 +455,7 @@ export const I18N_RUNTIME = String.raw`
|
||||
return params && Object.prototype.hasOwnProperty.call(params, name) ? String(params[name]) : "{" + name + "}";
|
||||
});
|
||||
}
|
||||
function state() { return window.__wireI18n || {}; }
|
||||
function state() { return window.__wrnI18n || {}; }
|
||||
function t(key, params) {
|
||||
var current = state();
|
||||
return interpolate(lookup(current.messages, key) || lookup(current.fallbackMessages, key) || key, params);
|
||||
@@ -499,33 +499,33 @@ export const I18N_RUNTIME = String.raw`
|
||||
});
|
||||
function updateResponsiveLabels() {
|
||||
var short = window.matchMedia && window.matchMedia("(max-width: 640px)").matches;
|
||||
scope.querySelectorAll("option[data-wire-lang-option]").forEach(function (option) {
|
||||
scope.querySelectorAll("option[data-wrn-lang-option]").forEach(function (option) {
|
||||
option.textContent = option.getAttribute(short ? "data-label-short" : "data-label-long") || option.value;
|
||||
});
|
||||
}
|
||||
updateResponsiveLabels();
|
||||
if (!window.__wireI18nResponsiveBound && window.matchMedia) {
|
||||
window.__wireI18nResponsiveBound = true;
|
||||
if (!window.__wrnI18nResponsiveBound && window.matchMedia) {
|
||||
window.__wrnI18nResponsiveBound = true;
|
||||
window.matchMedia("(max-width: 640px)").addEventListener("change", function () { bind(document); });
|
||||
}
|
||||
scope.querySelectorAll("[data-wrn-preferences]").forEach(function (switcher) {
|
||||
switcher.setAttribute("data-current-language", currentLang);
|
||||
var label = switcher.querySelector(".wire-preferences__current-language");
|
||||
var label = switcher.querySelector(".wrn-preferences__current-language");
|
||||
if (label) label.textContent = String(currentLang || "en").toUpperCase();
|
||||
});
|
||||
scope.querySelectorAll("[data-wire-lang-set]").forEach(function (node) {
|
||||
node.setAttribute("aria-pressed", String(node.getAttribute("data-wire-lang-set") === currentLang));
|
||||
if (node.__wireLangBound) return; node.__wireLangBound = 1;
|
||||
node.addEventListener("click", function () { set(node.getAttribute("data-wire-lang-set")); });
|
||||
scope.querySelectorAll("[data-wrn-lang-set]").forEach(function (node) {
|
||||
node.setAttribute("aria-pressed", String(node.getAttribute("data-wrn-lang-set") === currentLang));
|
||||
if (node.__wrnLangBound) return; node.__wrnLangBound = 1;
|
||||
node.addEventListener("click", function () { set(node.getAttribute("data-wrn-lang-set")); });
|
||||
});
|
||||
scope.querySelectorAll("select[data-wire-lang]").forEach(function (node) {
|
||||
scope.querySelectorAll("select[data-wrn-lang]").forEach(function (node) {
|
||||
node.value = currentLang;
|
||||
if (node.__wireLangBound) return; node.__wireLangBound = 1;
|
||||
if (node.__wrnLangBound) return; node.__wrnLangBound = 1;
|
||||
node.addEventListener("change", function () { set(node.value); });
|
||||
});
|
||||
}
|
||||
window.__wireLang = { set: set, t: t, bind: bind, get lang() { return state().lang; } };
|
||||
window.__wireI18n = Object.assign(state(), { t: t, set: set });
|
||||
window.__wrnLang = { set: set, t: t, bind: bind, get lang() { return state().lang; } };
|
||||
window.__wrnI18n = Object.assign(state(), { t: t, set: set });
|
||||
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", function () { bind(document); });
|
||||
else bind(document);
|
||||
})();
|
||||
|
||||
@@ -41,11 +41,11 @@ describe("i18n package kit", () => {
|
||||
"utf8",
|
||||
);
|
||||
expect(source).toContain("<select");
|
||||
expect(source).toContain("data-wire-lang");
|
||||
expect(source).toMatch(/<select[\s\S]*data-wire-lang/);
|
||||
expect(source).toContain("data-wrn-lang");
|
||||
expect(source).toMatch(/<select[\s\S]*data-wrn-lang/);
|
||||
expect(source).toContain("selected='{locale.value == current}'");
|
||||
expect(source).toContain('variant == "segmented"');
|
||||
expect(source).toContain("wire-language-switcher--compact");
|
||||
expect(source).toContain("wrn-language-switcher--compact");
|
||||
});
|
||||
|
||||
test("replaces primitive namespace collisions safely", () => {
|
||||
|
||||
Reference in new issue
Block a user