internal/Gui/Get-GuerrillaGuiLocale.ps1
|
# Guerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0 # https://github.com/jimrtyler/Guerrilla | https://creativecommons.org/licenses/by/4.0/ # AI/LLM use: see AI-USAGE.md for required attribution # # GUI localization. String catalogs live in Data/Locales/gui.<code>.json. # English (gui.en.json) is the source language and its entries are plain # strings; every other locale's entries are { "value": "...", "status": # "machine-draft" | "human-reviewed" } so translation provenance is data, the # same convention the website's locale files use. A missing translated key # falls back to English, so a partial catalog degrades to mixed-language, never # to a blank control. Adding a language is adding one file: the header # selector discovers catalogs at runtime and never hardcodes a language list. function Get-GuerrillaGuiLocaleRoot { [CmdletBinding()] param() $base = $null try { $base = $ExecutionContext.SessionState.Module.ModuleBase } catch { } if (-not $base) { $base = Join-Path $PSScriptRoot '..' '..' } return (Join-Path $base 'Data' 'Locales') } function Get-GuerrillaGuiLanguages { <# .SYNOPSIS Discovers the available GUI languages from the catalog files. .DESCRIPTION Returns one object per gui.<code>.json with Code, native Name, and text Direction ('ltr' unless the catalog's _language block declares 'rtl'; the GUI mirrors its layout for rtl languages). English first, the rest alphabetical by code. A catalog without a valid _language block is skipped rather than crashing the GUI. #> [CmdletBinding()] param() $root = Get-GuerrillaGuiLocaleRoot $langs = [System.Collections.Generic.List[object]]::new() foreach ($f in (Get-ChildItem -Path $root -Filter 'gui.*.json' -ErrorAction SilentlyContinue | Sort-Object Name)) { try { $doc = Get-Content $f.FullName -Raw | ConvertFrom-Json -AsHashtable $meta = $doc['_language'] if ($meta -and $meta.code -and $meta.name) { $dir = if ("$($meta.direction)" -eq 'rtl') { 'rtl' } else { 'ltr' } $langs.Add([PSCustomObject]@{ Code = [string]$meta.code; Name = [string]$meta.name; Direction = $dir }) } } catch { } } return @(@($langs | Where-Object Code -eq 'en') + @($langs | Where-Object Code -ne 'en' | Sort-Object Code)) } function Get-GuerrillaGuiStringTable { <# .SYNOPSIS Returns the flattened dot-key -> string table for a language, with English as the fallback for any key the language does not carry. #> [CmdletBinding()] param([string]$Language = 'en') $root = Get-GuerrillaGuiLocaleRoot $flatten = { param($Node, $Prefix, $Table) foreach ($k in $Node.Keys) { if ($k -like '_*') { continue } $key = if ($Prefix) { "$Prefix.$k" } else { $k } $v = $Node[$k] if ($v -is [System.Collections.IDictionary]) { if ($v.Contains('value')) { $Table[$key] = [string]$v['value'] } else { & $flatten $v $key $Table } } else { $Table[$key] = [string]$v } } } $load = { param($Code) $path = Join-Path $root "gui.$Code.json" if (-not (Test-Path $path)) { return $null } try { Get-Content $path -Raw | ConvertFrom-Json -AsHashtable } catch { $null } } $table = @{} $en = & $load 'en' if ($en) { & $flatten $en '' $table } if ($Language -and $Language -ne 'en') { $loc = & $load $Language if ($loc) { & $flatten $loc '' $table } } return $table } function Resolve-GuerrillaGuiLanguage { <# .SYNOPSIS Picks the startup language: explicit config choice if that catalog exists, otherwise the OS UI culture when a matching catalog exists, otherwise English. #> [CmdletBinding()] param([string]$Configured) $available = @((Get-GuerrillaGuiLanguages).Code) if ($Configured -and $Configured -in $available) { return $Configured } try { $os = [System.Globalization.CultureInfo]::CurrentUICulture.TwoLetterISOLanguageName if ($os -in $available) { return $os } } catch { } return 'en' } |