Skip to content
4 changes: 2 additions & 2 deletions WtProgram/ManagerViewService/Views/AppGroupingRulesView.fs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ type GroupingApplicationMenu() as this =
if index>=0 && index<shown.Length then picked.Trigger(shown.[index])
elif index=shown.Length then
use dialog = new OpenFileDialog(Title=tr Strings.AppRules.application,
Filter=tr Strings.AppRules.applicationFiles,CheckFileExists=true)
Filter=Localization.plain Strings.AppRules.applicationFiles,CheckFileExists=true)
if dialog.ShowDialog(owner)=DialogResult.OK then picked.Trigger(dialog.FileName))
choice <- Some next
dropDown <- next.CreateDropDown() |> Option.map(fun window ->
Expand Down Expand Up @@ -449,7 +449,7 @@ type AppGroupingRulesView() as this =
tree.Focus() |> ignore)
addGroup.Click.Add(fun _ ->
// Short and numbered from the start: Group 1, Group 2, the first number not taken.
let named number = tr (Strings.AppRules.newGroup number)
let named number = Localization.plain (Strings.AppRules.newGroup number)
let taken number = groups |> List.exists(fun group -> String.Equals(group.name,named number,StringComparison.OrdinalIgnoreCase))
let target = Seq.initInfinite ((+) 1) |> Seq.find(taken >> not) |> named
let group : AppGroupingTarget = {id=Guid.NewGuid().ToString("N");name=target}
Expand Down
16 changes: 12 additions & 4 deletions WtProgram/Shared/Localization.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ open System.Globalization

/// One piece of user-visible text in every supported language. Every field is required, so a
/// missing translation is a compile error. All instances live in Strings.
type LocalizedText = { en:string; zh:string; ja:string }
type LocalizedText = { en:string; zh:string; ja:string; ar:string }

/// All user-visible text is compiled into the single WindowTabs.exe. (Satellite resource
/// assemblies cannot be statically linked, so a .resx translation would not ship.)
module Localization =
/// Supported languages: code, name shown in the language menu, compact label.
let languages = [| "en","English","EN"; "zh","中文","中"; "ja","日本語","日" |]
let languages = [| "en","English","EN"; "zh","中文","中"; "ja","日本語","日"; "ar","العربية","ع" |]
/// Values of the language setting: "system" follows the Windows display language.
let preferences = "system" :: [for code,_,_ in languages -> code]
let mutable private preference = "system"
Expand All @@ -18,13 +18,21 @@ module Localization =
let current() =
let code = if preference="system" then CultureInfo.CurrentUICulture.TwoLetterISOLanguageName else preference
if languages |> Array.exists (fun (c,_,_) -> c=code) then code else "en"
let tr (text:LocalizedText) =
/// The text in the current language as written, for values that are parsed or saved.
let plain (text:LocalizedText) =
match current() with
| "zh" -> text.zh
| "ja" -> text.ja
| "ar" -> text.ar
| _ -> text.en
/// GDI and the standard controls read text left to right unless told otherwise, which puts the
/// words of an Arabic sentence around a Latin word, and its final full stop, in the wrong order.
/// A right-to-left embedding keeps each text in reading order without mirroring the layout.
let tr (text:LocalizedText) =
let value = plain text
if current()="ar" && value<>"" then "\u202B"+value+"\u202C" else value
/// Every translation of a text, for search.
let all (text:LocalizedText) = [text.en; text.zh; text.ja]
let all (text:LocalizedText) = [text.en; text.zh; text.ja; text.ar]

[<AutoOpen>]
module LocalizationOperators =
Expand Down
Loading
Loading