NS12 Locale Router Plugin Blueprint
Updated: 2026-04-24
Metadata
| Field | Value |
|---|---|
| Source site | aiwikis.org |
| Source URL | https://aiwikis.org/ |
| Canonical AIWikis URL | https://aiwikis.org/files/aiwikis/raw-system-archives-uaix-internal-memory-reorg-2026-05-01-docs-ns12-loca-4bc5eda5/ |
| Source reference | raw/system-archives/uaix/internal-memory-reorg/2026-05-01/docs/ns12_locale_router_plugin_blueprint.md |
| File type | md |
| Content category | memory-file |
| Last fetched | 2026-05-02T01:47:31.8867765Z |
| Last changed | 2026-04-24T01:44:43.1966006Z |
| Content hash | sha256:4bc5eda5a5ec5724db1c050147be213c9ac0919944e2140c232ec66521d8437a |
| Import status | unchanged |
| Raw source layer | data/sources/aiwikis/raw-system-archives-uaix-internal-memory-reorg-2026-05-01-docs-ns12-locale-router-plugin-bluepri-4bc5eda5a5ec.md |
| Normalized source layer | data/normalized/aiwikis/raw-system-archives-uaix-internal-memory-reorg-2026-05-01-docs-ns12-locale-router-plugin-bluepri-4bc5eda5a5ec.txt |
Current File Content
Structure Preview
- NS12 Locale Router Plugin Blueprint
- Status
- How To Use This Document
- Purpose
- Goal
- Important reality check
- Standards and WordPress hooks to rely on
- Recommended plugin architecture
- Core data model
- Supported locale record
- Dictionary file structure
- Dictionary template file
- Routing design
- Request rules
- Rewrite strategy
- Better production approach
- Locale resolution order
- Front end
- Admin
- REST API
- Link rewriting rules
- Rewrite these sources
- Do not rewrite
- URL localization example
Raw Version
# NS12 Locale Router Plugin Blueprint
Updated: 2026-04-24
## Status
This is a design and implementation blueprint.
It is useful background, but the implemented plugin source is the authority for current runtime behavior.
## How To Use This Document
- Use this file to understand the intended architecture and design rationale for `ns12-locale-router`.
- Prefer the live plugin source for implemented behavior.
- Prefer `docs/deploy.md` for current deployment and routing policy.
- Treat this as design background, not the current feature backlog. Use `docs/roadmap.md` for future priorities.
## Purpose
This document captures the design intent, data model, and implementation direction for `ns12-locale-router` without replacing the live plugin source as the authority for current behavior.
Current companion references:
- `docs/navigation/implementation-and-runtime.md` for the shortest runtime and ownership path
- `wp-content/plugins/README.md` for plugin-area traversal
- `docs/runtime-architecture.md` for architecture-risk and bootstrap context
- `docs/deploy.md` for current routing and release policy
- `docs/ns12-locale-router-market-research.md` for product and adoption framing
## Goal
Create a WordPress plugin with these properties:
- 100% URL-based locale routing.
- No cookies.
- No session state.
- No JavaScript required for locale persistence.
- Locale segment always first in the path: `/{locale}/...`
- Switching locale on `/en-us/stuff` sends the visitor to `/es-us/stuff`
- Internal links are rewritten automatically so the user stays inside the current locale until they explicitly switch
- Translation content lives in uploaded JSON dictionaries, one file per locale
- Admin tools verify dictionary completeness, key alignment, placeholder integrity, and translation quality workflow status
---
## Important reality check
A plugin should **not** hardcode a supposedly complete finite list of “all locale codes in the world.†The safe standard is to support **BCP 47 style locale tags** and maintain an admin-managed list of **supported locales**. Unicode CLDR is the practical source of locale data, and WordPress itself only knows about language files actually present on the site. WordPress also exposes hooks for request locale determination and locale switching. This means the plugin should:
1. Accept valid locale tags.
2. Let admins enable only the locales they actually want.
3. Validate locale tags against a shipped standards dataset.
4. Support importing a full locale catalog for selection in admin.
---
## Standards and WordPress hooks to rely on
- `determine_locale` / `pre_determine_locale` for request locale override.
- `switch_to_locale()` for request-time locale switching.
- `get_available_languages()` for WordPress-installed translation packs.
- Rewrite rules to capture the locale path prefix.
- URL filters such as `home_url`, `post_type_link`, `page_link`, `term_link`, `nav_menu_link_attributes`, and content-level URL rewriting where needed.
---
## Recommended plugin architecture
```text
/wp-content/plugins/ns12-locale-router/
ns12-locale-router.php
/src/
Plugin.php
Router.php
LocaleRepository.php
LocaleValidator.php
DictionaryRepository.php
DictionaryValidator.php
LinkLocalizer.php
Switcher.php
Admin/AdminPages.php
Admin/SettingsPage.php
Admin/DictionariesPage.php
Admin/ValidationPage.php
Support/Path.php
Support/Json.php
Support/Html.php
/data/
cldr-locales.json
default-supported-locales.json
/languages/
/templates/
switcher.php
/assets/
admin.css
front.css
```
---
## Core data model
### Supported locale record
```json
{
"tag": "en-US",
"urlTag": "en-us",
"language": "English",
"nativeName": "English (United States)",
"region": "US",
"script": "Latn",
"isRtl": false,
"enabled": true,
"isDefault": true,
"fallback": "en-US"
}
```
### Dictionary file structure
One JSON file per locale, for example `en-US.json`:
```json
{
"meta": {
"locale": "en-US",
"version": 1,
"fallback": "en-US",
"generatedFrom": "template",
"qualityStatus": "approved"
},
"strings": {
"site.header.home": "Home",
"site.header.about": "About",
"site.footer.contact": "Contact",
"page.contact.title": "Contact Us",
"page.contact.intro": "Send us a message",
"component.search.placeholder": "Search"
}
}
```
### Dictionary template file
This is the source language master file. Every other locale must match its keys exactly.
```json
{
"meta": {
"locale": "template",
"version": 1
},
"strings": {
"site.header.home": "Home",
"site.header.about": "About",
"site.footer.contact": "Contact"
}
}
```
---
## Routing design
### Request rules
- `/` redirects to `/{default-locale}/`
- `/about/` redirects to `/{default-locale}/about/`
- `/en-us/` serves homepage in locale `en-US`
- `/en-us/about/` serves the same content object as `/about/`, but under locale `en-US`
- `/en-us/wp-admin/` is **not** routed through the public locale router
- `/wp-json/`, `/wp-admin/`, `/wp-login.php`, `/feed/`, sitemap endpoints, and static assets must be excluded or handled carefully
### Rewrite strategy
Use a rewrite tag and top-priority rules:
```php
add_rewrite_tag('%ns12_locale%', '([A-Za-z]{2,8}(?:-[A-Za-z0-9]{2,8})*)');
add_rewrite_rule('^([A-Za-z]{2,8}(?:-[A-Za-z0-9]{2,8})?)/(.*)?$', 'index.php?ns12_locale=$matches[1]&pagename=$matches[2]', 'top');
```
That exact example is only a starting point. In production, the plugin should parse the locale prefix first, then hand the remainder back to WordPress request parsing instead of overloading only `pagename`.
### Better production approach
- Parse request URI very early.
- Detect whether first path segment is a supported locale.
- Store current locale in request scope only.
- Strip locale segment before WordPress resolves the route.
- Rebuild canonical URLs with locale prefix later.
This is more reliable than trying to map every request type through one rewrite target.
---
## Locale resolution order
### Front end
1. Locale path segment if present and supported.
2. Default locale if absent.
3. Redirect to localized URL if locale prefix is absent.
### Admin
- Do **not** use front-end locale path switching for admin screens.
- Admin should stay under the user’s WordPress locale or plugin admin preference.
### REST API
Two choices should be configurable:
1. Keep REST unlocalized.
2. Accept locale-prefixed REST routes for front-end consumers.
Default: keep REST unlocalized for stability.
---
## Link rewriting rules
The plugin must keep the user inside the active locale automatically.
### Rewrite these sources
- `home_url()`
- `site_url()` where appropriate for front-end links
- post permalinks
- page permalinks
- term links
- author links
- menu item URLs
- pagination links
- breadcrumbs if using common filters
- content links in post content output
- logo/home links
- canonical links
- hreflang tags
### Do not rewrite
- admin URLs
- login URLs unless explicitly enabled
- AJAX endpoints unless explicitly enabled
- external links
- mailto, tel, javascript, fragment-only links
- static file URLs
### URL localization example
Current request: `/en-us/products/widget-a/`
- Home link becomes `/en-us/`
- About link becomes `/en-us/about/`
- Contact link becomes `/en-us/contact/`
- Locale switch to Spanish becomes `/es-us/products/widget-a/`
---
## Translation retrieval model
Use a thin translation layer instead of relying only on native WordPress gettext.
### Why
You want every string controlled by a shared dictionary-key system and uploaded JSON files.
### API
```php
function ns12lr_t(String $key, String $fallback = ''): String;
function ns12lr_t_format(String $key, array $replacements, String $fallback = ''): String;
```
### Example usage
```php
echo esc_html(ns12lr_t('site.header.home', 'Home'));
```
### Placeholder enforcement
All placeholders must match across locales:
- `{name}`
- `{count}`
- `{city}`
Validator must reject translations that are missing placeholders or introduce unknown ones.
---
## Admin features
### 1. Settings page
- Enable plugin
- Default locale
- Enabled locales
- URL casing strategy
- Trailing slash strategy
- Redirect behavior for bare URLs
- REST localization mode
- Exclusion patterns
- Fallback locale per locale
- Toggle content link rewriting
### 2. Locale catalog page
- Searchable full locale list
- Add locale to supported set
- Mark default
- Set fallback
- View native name, region, script, RTL flag
- Bulk enable or disable
### 3. Dictionaries page
- Upload locale JSON
- Download template JSON
- View missing keys
- View extra keys
- View placeholder mismatches
- View empty translations
- View untranslated values identical to template
- View last validation result
- Version mismatch warnings
### 4. Validation page
Run full verification suite:
- Missing keys
- Extra keys
- Type mismatches
- Placeholder mismatches
- Invalid JSON
- Invalid locale metadata
- Duplicate keys
- Empty values
- HTML tag mismatches if HTML is allowed
- Variable interpolation mismatches
- Fallback coverage
- Suspected machine-translation artifacts
- Length outliers
- Encoding issues
### 5. Quality workflow page
Each uploaded dictionary can have status:
- draft
- ai-generated
- reviewed
- approved
- rejected
Also record:
- uploader
- upload time
- reviewer
- approval time
- comparison against template version
---
## Content strategy
There are two separate content types to handle.
### A. Theme and plugin UI strings
Use dictionary keys directly in templates and plugin output.
### B. Page and post content
This is the harder part.
For a robust first version, support these modes:
1. **Dictionary token mode**
Authors place tokens like `{{page.contact.title}}` in content.
Render pipeline replaces them with localized strings.
2. **Localized meta mode**
Store translated content per locale in post meta.
3. **Block attribute mode**
For custom blocks, store dictionary keys and render localized text.
Do **not** promise perfect automatic extraction of every existing text fragment in arbitrary HTML content. That becomes fragile fast.
---
## Validation rules for uploaded dictionaries
### Required checks
1. Locale tag matches filename.
2. Locale tag is valid against locale catalog.
3. `meta.version` matches template version.
4. Every key from template exists.
5. No unknown keys unless explicitly allowed.
6. Every value is a string.
7. Required placeholders exactly match template.
8. No control character corruption.
9. JSON is UTF-8.
10. File size and key count are within expected bounds.
11. Fallback locale exists.
12. Strings that should remain unchanged stay unchanged where required.
### Recommended quality checks
1. Detect same-as-source ratio.
2. Detect very short outputs where source is long.
3. Detect repeated punctuation or malformed unicode.
4. Detect suspicious placeholder movement.
5. Detect likely truncation.
6. Detect markup imbalance.
---
## Locale switcher UX
### Requirements
- No JS required.
- Native `<select>` inside a `<form method="get">` or `<form method="post">` is fine, but switching itself should produce a normal URL navigation.
- Nice looking and accessible.
- Keeps current path.
- Preserves query string where appropriate.
### Best no-JS pattern
Use a `<form method="get" action="">` with a `<select name="switch_locale">` and submit button.
For a better look without JavaScript, style the select and visually minimize the submit button.
### Example behavior
Current URL:
`/en-us/stuff/?page=2`
User selects `es-us`
Server redirects to:
`/es-us/stuff/?page=2`
---
## SEO requirements
- Every localized page must emit self canonical in its locale URL.
- Emit `hreflang` tags for all enabled sibling locales.
- Emit `x-default` to default locale.
- Prevent duplicate content between bare and localized URLs by redirecting bare URLs.
- Generate locale-aware sitemaps if enabled.
---
## Performance requirements
- Cache parsed dictionaries in object cache/transients.
- Cache key inventory and validation results.
- Avoid parsing JSON on every request if unchanged.
- Store a normalized dictionary index in an option or custom table.
- Use a hash of file contents to invalidate caches.
- Keep URL rewrite logic early and lightweight.
---
## Reliability guardrails
- Never infer locale from cookie or browser header if URL mode is strict.
- Never silently switch locale mid-session.
- Never output a non-prefixed internal URL on the front end once a localized request is active.
- Never localize excluded endpoints.
- Fail closed on broken dictionaries by falling back to source locale and reporting admin errors.
---
## Recommended database footprint
Use options for settings and a custom table for validation history.
### Options
- `ns12lr_settings`
- `ns12lr_supported_locales`
- `ns12lr_default_locale`
- `ns12lr_dictionary_manifest`
### Custom table
`{prefix}ns12lr_dictionary_validation`
Columns:
- `ValidationId`
- `LocaleTag`
- `FileHash`
- `TemplateVersion`
- `Status`
- `IssueCount`
- `IssuesJson`
- `CreatedUtc`
- `CreatedByUserId`
---
## Minimal implementation plan
### Phase 1
- Supported locale registry
- Locale path detection
- Redirect bare URLs to default locale
- Locale-aware permalink rewriting
- Simple switcher
- JSON dictionary loader
- Key validation
### Phase 2
- Content token rendering
- Admin upload UI
- Full validation reporting
- hreflang output
- locale-aware sitemap integration
### Phase 3
- Per-post localized content storage
- glossary/translation memory support
- AI-assisted validation pipeline
- export/import tooling
---
## Recommended first-cut PHP files
## `ns12-locale-router.php`
```php
<?php
/**
* Plugin Name: NS12 Locale Router
* Description: URL-based locale routing and JSON dictionary localization for WordPress.
* Version: 0.1.0
* Author: Your Name
*/
if (!defined('ABSPATH')) {
exit;
}
require_once __DIR__ . '/src/Plugin.php';
\Ns12LocaleRouter\Plugin::boot(__FILE__);
```
## `src/Plugin.php`
```php
<?php
namespace Ns12LocaleRouter;
final class Plugin
{
private static String $pluginFile;
public static function boot(String $pluginFile): void
{
self::$pluginFile = $pluginFile;
add_action('init', array(__CLASS__, 'registerHooks'));
register_activation_hook($pluginFile, array(__CLASS__, 'activate'));
register_deactivation_hook($pluginFile, array(__CLASS__, 'deactivate'));
}
public static function activate(): void
{
Router::registerRewriteRules();
flush_rewrite_rules();
}
public static function deactivate(): void
{
flush_rewrite_rules();
}
public static function registerHooks(): void
{
Router::register();
LinkLocalizer::register();
Switcher::register();
Admin\AdminPages::register();
}
}
```
## `src/Router.php`
```php
<?php
namespace Ns12LocaleRouter;
final class Router
{
public static function register(): void
{
add_action('init', array(__CLASS__, 'registerRewriteRules'));
add_filter('query_vars', array(__CLASS__, 'registerQueryVars'));
add_action('parse_request', array(__CLASS__, 'parseRequest'), 1);
add_filter('determine_locale', array(__CLASS__, 'filterDetermineLocale'), 1);
add_action('template_redirect', array(__CLASS__, 'redirectBareUrls'), 1);
}
public static function registerRewriteRules(): void
{
add_rewrite_tag('%ns12_locale%', '([A-Za-z]{2,8}(?:-[A-Za-z0-9]{2,8})*)');
}
public static function registerQueryVars(array $queryVars): array
{
$queryVars[] = 'ns12_locale';
return $queryVars;
}
public static function parseRequest(\WP $wp): void
{
if (is_admin()) {
return;
}
$requestPath = self::getRequestPath();
$segments = array_values(array_filter(explode('/', trim($requestPath, '/'))));
if (count($segments) === 0) {
return;
}
$possibleLocale = LocaleRepository::normalizeUrlTag($segments[0]);
if (!LocaleRepository::isEnabledUrlTag($possibleLocale)) {
return;
}
$wp->query_vars['ns12_locale'] = $possibleLocale;
array_shift($segments);
$wp->request = implode('/', $segments);
$_SERVER['REQUEST_URI'] = '/' . implode('/', $segments);
}
public static function filterDetermineLocale(String $locale): String
{
if (is_admin()) {
return $locale;
}
$urlLocale = get_query_var('ns12_locale');
if (empty($urlLocale)) {
return $locale;
}
$resolvedLocale = LocaleRepository::urlTagToWpLocale($urlLocale);
if (empty($resolvedLocale)) {
return $locale;
}
switch_to_locale($resolvedLocale);
return $resolvedLocale;
}
public static function redirectBareUrls(): void
{
if (is_admin() || wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) {
return;
}
$requestPath = self::getRequestPath();
$segments = array_values(array_filter(explode('/', trim($requestPath, '/'))));
if (!empty($segments) && LocaleRepository::isEnabledUrlTag(LocaleRepository::normalizeUrlTag($segments[0]))) {
return;
}
$defaultUrlTag = LocaleRepository::getDefaultUrlTag();
$redirectPath = '/' . $defaultUrlTag . '/' . ltrim($requestPath, '/');
$redirectPath = preg_replace('#/+#', '/', $redirectPath);
$queryString = isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] !== ''
? '?' . $_SERVER['QUERY_STRING']
: '';
wp_safe_redirect(home_url($redirectPath) . $queryString, 301);
exit;
}
private static function getRequestPath(): String
{
$requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
$path = parse_url($requestUri, PHP_URL_PATH);
return is_string($path) ? $path : '/';
}
}
```
## `src/LocaleRepository.php`
```php
<?php
namespace Ns12LocaleRouter;
final class LocaleRepository
{
public static function getDefaultUrlTag(): String
{
$settings = get_option('ns12lr_settings', array());
$defaultLocale = isset($settings['default_locale']) ? (String)$settings['default_locale'] : 'en-US';
return self::localeToUrlTag($defaultLocale);
}
public static function localeToUrlTag(String $locale): String
{
return strtolower(str_replace('_', '-', $locale));
}
public static function normalizeUrlTag(String $urlTag): String
{
return strtolower(trim($urlTag));
}
public static function urlTagToWpLocale(String $urlTag): String
{
$supported = self::getSupportedLocales();
foreach ($supported as $locale) {
if (self::localeToUrlTag($locale['tag']) === self::normalizeUrlTag($urlTag)) {
return str_replace('-', '_', $locale['tag']);
}
}
return '';
}
public static function isEnabledUrlTag(String $urlTag): bool
{
$normalized = self::normalizeUrlTag($urlTag);
foreach (self::getSupportedLocales() as $locale) {
if (!empty($locale['enabled']) && self::localeToUrlTag($locale['tag']) === $normalized) {
return true;
}
}
return false;
}
public static function getSupportedLocales(): array
{
$supported = get_option('ns12lr_supported_locales');
if (is_array($supported) && count($supported) > 0) {
return $supported;
}
return array(
array(
'tag' => 'en-US',
'enabled' => true,
'isDefault' => true,
'nativeName' => 'English (United States)'
)
);
}
}
```
## `src/DictionaryRepository.php`
```php
<?php
namespace Ns12LocaleRouter;
final class DictionaryRepository
{
public static function translate(String $key, String $fallback = ''): String
{
$locale = determine_locale();
$dictionary = self::loadLocaleDictionary($locale);
if (isset($dictionary['strings'][$key]) && is_string($dictionary['strings'][$key]) && $dictionary['strings'][$key] !== '') {
return $dictionary['strings'][$key];
}
return $fallback !== '' ? $fallback : $key;
}
public static function loadLocaleDictionary(String $locale): array
{
$cacheKey = 'ns12lr_dict_' . md5($locale);
$cached = wp_cache_get($cacheKey, 'ns12lr');
if (is_array($cached)) {
return $cached;
}
$uploadDir = wp_upload_dir();
$filePath = trailingslashit($uploadDir['basedir']) . 'ns12-locale-router/dictionaries/' . $locale . '.json';
if (!file_exists($filePath)) {
$dictionary = array('meta' => array('locale' => $locale), 'strings' => array());
wp_cache_set($cacheKey, $dictionary, 'ns12lr');
return $dictionary;
}
$json = file_get_contents($filePath);
$data = json_decode($json, true);
if (!is_array($data)) {
$data = array('meta' => array('locale' => $locale), 'strings' => array());
}
wp_cache_set($cacheKey, $data, 'ns12lr');
return $data;
}
}
```
## `src/LinkLocalizer.php`
```php
<?php
namespace Ns12LocaleRouter;
final class LinkLocalizer
{
public static function register(): void
{
add_filter('home_url', array(__CLASS__, 'filterHomeUrl'), 20, 4);
add_filter('page_link', array(__CLASS__, 'filterLocalizedUrl'), 20);
add_filter('post_link', array(__CLASS__, 'filterLocalizedUrl'), 20);
add_filter('post_type_link', array(__CLASS__, 'filterLocalizedUrl'), 20);
add_filter('term_link', array(__CLASS__, 'filterLocalizedUrl'), 20);
add_filter('nav_menu_link_attributes', array(__CLASS__, 'filterMenuLinkAttributes'), 20, 4);
add_filter('the_content', array(__CLASS__, 'filterContentLinks'), 20);
}
public static function filterHomeUrl(String $url, String $path, ?String $origScheme, ?Int32 $blogId): String
{
return self::localizeUrl($url);
}
public static function filterLocalizedUrl(String $url): String
{
return self::localizeUrl($url);
}
public static function filterMenuLinkAttributes(array $atts): array
{
if (isset($atts['href']) && is_string($atts['href'])) {
$atts['href'] = self::localizeUrl($atts['href']);
}
return $atts;
}
public static function filterContentLinks(String $content): String
{
if ($content === '') {
return $content;
}
return preg_replace_callback(
'#href=("|\')(.*?)\1#i',
function (array $matches): String {
$quote = $matches[1];
$url = $matches[2];
$localizedUrl = self::localizeUrl($url);
return 'href=' . $quote . esc_url($localizedUrl) . $quote;
},
$content
);
}
public static function localizeUrl(String $url): String
{
if ($url === '' || self::isExcludedUrl($url)) {
return $url;
}
$currentUrlTag = get_query_var('ns12_locale');
if (!is_string($currentUrlTag) || $currentUrlTag === '') {
$currentUrlTag = LocaleRepository::getDefaultUrlTag();
}
$homeUrl = home_url('/');
if (strpos($url, $homeUrl) !== 0) {
return $url;
}
$relativePath = substr($url, strlen(rtrim($homeUrl, '/')));
$relativePath = '/' . ltrim($relativePath, '/');
$segments = array_values(array_filter(explode('/', trim($relativePath, '/'))));
if (!empty($segments) && LocaleRepository::isEnabledUrlTag(LocaleRepository::normalizeUrlTag($segments[0]))) {
$segments[0] = $currentUrlTag;
return home_url('/' . implode('/', $segments) . '/');
}
return home_url('/' . $currentUrlTag . trim($relativePath, '/') . (str_ends_with($relativePath, '/') ? '/' : ''));
}
private static function isExcludedUrl(String $url): bool
{
if (preg_match('#^(mailto:|tel:|javascript:|#)#i', $url)) {
return true;
}
if (preg_match('#^https?://#i', $url) && strpos($url, home_url('/')) !== 0) {
return true;
}
return false;
}
}
```
## `src/Switcher.php`
```php
<?php
namespace Ns12LocaleRouter;
final class Switcher
{
public static function register(): void
{
add_action('init', array(__CLASS__, 'handleSwitchRequest'));
add_shortcode('ns12_locale_switcher', array(__CLASS__, 'renderShortcode'));
}
public static function handleSwitchRequest(): void
{
if (!isset($_GET['ns12lr_switch_locale'])) {
return;
}
$targetUrlTag = sanitize_text_field(wp_unslash($_GET['ns12lr_switch_locale']));
$targetUrlTag = LocaleRepository::normalizeUrlTag($targetUrlTag);
if (!LocaleRepository::isEnabledUrlTag($targetUrlTag)) {
return;
}
$currentPath = isset($_GET['ns12lr_return_path']) ? wp_unslash($_GET['ns12lr_return_path']) : '/';
$currentPath = is_string($currentPath) ? $currentPath : '/';
$segments = array_values(array_filter(explode('/', trim($currentPath, '/'))));
if (!empty($segments) && LocaleRepository::isEnabledUrlTag(LocaleRepository::normalizeUrlTag($segments[0]))) {
$segments[0] = $targetUrlTag;
} else {
array_unshift($segments, $targetUrlTag);
}
$redirectPath = '/' . implode('/', $segments) . '/';
wp_safe_redirect(home_url($redirectPath));
exit;
}
public static function renderShortcode(): String
{
$currentPath = isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) : '/';
$currentUrlTag = get_query_var('ns12_locale');
if (!is_string($currentUrlTag) || $currentUrlTag === '') {
$currentUrlTag = LocaleRepository::getDefaultUrlTag();
}
$locales = LocaleRepository::getSupportedLocales();
ob_start();
?>
<form method="get" action="" class="ns12lr-switcher-form">
<label for="ns12lr_switch_locale">Language</label>
<select id="ns12lr_switch_locale" name="ns12lr_switch_locale">
<?php foreach ($locales as $locale) : ?>
<?php $urlTag = LocaleRepository::localeToUrlTag($locale['tag']); ?>
<option value="<?php echo esc_attr($urlTag); ?>" <?php selected($urlTag, $currentUrlTag); ?>>
<?php echo esc_html($locale['nativeName']); ?>
</option>
<?php endforeach; ?>
</select>
<input type="hidden" name="ns12lr_return_path" value="<?php echo esc_attr((String)$currentPath); ?>" />
<button type="submit">Go</button>
</form>
<?php
return (String)ob_get_clean();
}
}
```
---
## Critical corrections before production
The code above is a **blueprint starter**, not the final production plugin. Before production, these points must be fixed:
1. Request stripping should not mutate `REQUEST_URI` directly without broader compatibility testing.
2. Link localization needs stricter path joining.
3. Rewrite and canonical handling must cover custom post types, archives, taxonomies, search, pagination, and feeds.
4. Admin settings and upload workflow need nonce, capability, and filesystem hardening.
5. Locale validation should use a standards dataset, not only the enabled-locale list.
6. Translation rendering for post content needs a defined content model instead of naive global replacement.
7. `switch_to_locale()` should be called carefully so the request locale stack is not misused.
8. Upload validation and verification reports need full implementation.
---
## Recommendation
Build this plugin as a **two-part system**:
- **URL Locale Core**: routing, locale persistence, link rewriting, switcher, SEO
- **Dictionary Manager**: locale catalog, uploads, validation, reporting, translation API
That split will make it more stable, easier to test, and much less likely to glitch.
---
## What I would build next
1. A production-ready file-by-file plugin scaffold.
2. The admin pages and upload validators.
3. The locale catalog dataset import pipeline.
4. The content token rendering system.
5. A test matrix for posts, pages, categories, menus, search, archives, pagination, and canonical behavior.
Why This File Exists
This is a memory-system evidence file from aiwikis.org. It is shown here because AIWikis.org is demonstrating the real source files that make the UAIX / LLM Wiki memory system work, not only summarizing those systems after the fact.
Role
This file is memory-system evidence. It records source history, archive transfer, intake disposition, or another piece of provenance that should be retrievable without becoming an unsupported public claim.
Structure
The file is structured around these visible headings: NS12 Locale Router Plugin Blueprint; Status; How To Use This Document; Purpose; Goal; Important reality check; Standards and WordPress hooks to rely on; Recommended plugin architecture. Those headings are retrieval anchors: a crawler or LLM can decide whether the file is relevant before reading every line.
Prompt-Size And Retrieval Benefit
Keeping this material in a separate file reduces prompt pressure because an agent can load this exact unit only when its role, source site, category, or hash is relevant. The surrounding index pages point to it, while this page preserves the full content for audit and exact recall.
How To Use It
- Humans should read the metadata first, then inspect the raw content when they need exact wording or provenance.
- LLMs and agents should use the source site, category, hash, headings, and related files to decide whether this file belongs in the active prompt.
- Crawlers should treat the AIWikis page as transparent evidence and follow the source URL/source reference for authority boundaries.
- Future maintainers should regenerate this page whenever the source hash changes, then review the explanation if the role or structure changed.
Update Requirements
When this source file changes, update the raw source layer, normalized source layer, hash history, this rendered page, generated explanation, source-file inventory, changed-files report, and any source-section index that links to it.
Related Pages
Provenance And History
- Current observation:
2026-05-02T01:47:31.8867765Z - Source origin:
current-source-workspace - Retrieval method:
local-source-workspace - Duplicate group:
sfg-137(primary) - Historical hash records are stored in
data/hashes/source-file-history.jsonl.
Machine-Readable Metadata
{
"title": "NS12 Locale Router Plugin Blueprint",
"source_site": "aiwikis.org",
"source_url": "https://aiwikis.org/",
"canonical_url": "https://aiwikis.org/files/aiwikis/raw-system-archives-uaix-internal-memory-reorg-2026-05-01-docs-ns12-loca-4bc5eda5/",
"source_reference": "raw/system-archives/uaix/internal-memory-reorg/2026-05-01/docs/ns12_locale_router_plugin_blueprint.md",
"file_type": "md",
"content_category": "memory-file",
"content_hash": "sha256:4bc5eda5a5ec5724db1c050147be213c9ac0919944e2140c232ec66521d8437a",
"last_fetched": "2026-05-02T01:47:31.8867765Z",
"last_changed": "2026-04-24T01:44:43.1966006Z",
"import_status": "unchanged",
"duplicate_group_id": "sfg-137",
"duplicate_role": "primary",
"related_files": [
],
"generated_explanation": true,
"explanation_last_generated": "2026-05-02T01:47:31.8867765Z"
}