diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bd6e8ab7..18c47278 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -3,8 +3,6 @@ name: Release
on:
push:
branches: [ main ]
- pull_request:
- branches: [ main ]
jobs:
# Separate build job for JavaScript
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 646533bc..7e573d14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,5 @@
# Changelog
+
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
@@ -6,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
+## 4.3.0
+
+### Added
+
+* Add localization support
+
## 4.2.0
### Fixed
diff --git a/build.fsx b/build.fsx
index 7cdf1095..82608f0a 100644
--- a/build.fsx
+++ b/build.fsx
@@ -122,7 +122,7 @@ module Stages =
)
}
- let donetRestore =
+ let dotnetRestore =
stage "Restore .NET dependencies" { run "dotnet restore" }
let npmInstall = stage "NPM install" { run "npm install" }
@@ -131,7 +131,7 @@ module Stages =
stage "NPM install" {
whenCmd {
name "--local"
- description "Build using local pacakges from fable repository"
+ description "Build using local packages from fable repository"
}
run
@@ -260,7 +260,7 @@ module Stages =
pipeline "WatchApp" {
Stages.clean
- Stages.donetRestore
+ Stages.dotnetRestore
Stages.npmInstall
// We don't need to call unlink because npm install always reset
// the dependencies so they are not linked anymore and will be linked
@@ -278,7 +278,7 @@ pipeline "WatchApp" {
pipeline "BuildApp" {
Stages.clean
- Stages.donetRestore
+ Stages.dotnetRestore
Stages.npmInstall
Stages.npmLink
Stages.copyModules
@@ -321,14 +321,14 @@ pipeline "Release" {
whenBranch "main"
Stages.clean
- Stages.donetRestore
+ Stages.dotnetRestore
Stages.npmInstall
Stages.copyModules
Stages.buildApp
Stages.updatePreludeREPLVersion
- // When releasing locall we use the gh-pages CLI tool
+ // When releasing local we use the gh-pages CLI tool
// When releasing on CI we use the corresponding Github Action
stage "Push to gh-pages (local)" {
whenNot {
@@ -343,7 +343,7 @@ pipeline "Release" {
pipeline "BuildLib" {
Stages.clean
- Stages.donetRestore
+ Stages.dotnetRestore
Stages.npmInstall
Stages.copyModules
diff --git a/paket.dependencies b/paket.dependencies
index 69666c0f..2457303c 100644
--- a/paket.dependencies
+++ b/paket.dependencies
@@ -17,9 +17,10 @@ nuget Feliz prerelease
nuget Feliz.Bulma
nuget Feliz.Bulma.Tooltip
nuget Fable.Browser.Css
-nuget Fable.Browser.Dom
-nuget Fable.Browser.Event
+nuget Fable.Browser.Dom == 2.4.4
+nuget Fable.Browser.Event == 1.4.5
nuget Fable.Browser.MediaQueryList
+nuget Fable.Browser.Navigator == 2.0.0
# REPL Lib
@@ -32,8 +33,8 @@ nuget Fable.Browser.MediaQueryList
#github fulma/Fulma:1e763d852112307370675a0cf692415a53d1993f#
github elmish/elmish:v3.x
-github fable-compiler/fable-promise:master
+github fable-compiler/fable-promise:14eca483d664dce5aebbe50fb92a536a7c1ffe53
github alfonsogarciacaro/Feliz.Engine:main
github alfonsogarciacaro/Feliz.Snabbdom:main
-github davedawkins/Feliz.Engine.Bulma:main
-github davedawkins/Sutil:main
+github davedawkins/Feliz.Engine.Bulma:0df54855ac3464a432cd207412ab66e650a87d77
+github davedawkins/Sutil:343bc31867127638a79afede0dcaa97ca56fa264
diff --git a/paket.lock b/paket.lock
index b363b3f9..416ec720 100644
--- a/paket.lock
+++ b/paket.lock
@@ -11,24 +11,39 @@ NUGET
Fable.Browser.Svg (>= 2.3)
Fable.Core (>= 3.0)
FSharp.Core (>= 4.7.2)
- Fable.Browser.Dom (2.14)
- Fable.Browser.Blob (>= 1.3)
- Fable.Browser.Event (>= 1.5)
- Fable.Browser.WebStorage (>= 1.2)
- Fable.Core (>= 3.2.8)
+ Fable.Browser.Dom (2.4.4)
+ Fable.Browser.Blob (>= 1.1.4)
+ Fable.Browser.Event (>= 1.4.4)
+ Fable.Browser.WebStorage (>= 1.0.4)
+ Fable.Core (>= 3.0)
FSharp.Core (>= 4.7.2)
- Fable.Browser.Event (1.5)
- Fable.Browser.Gamepad (>= 1.1)
+ Fable.Browser.Event (1.4.5)
+ Fable.Browser.Gamepad (>= 1.0.3)
Fable.Core (>= 3.0)
FSharp.Core (>= 4.7.2)
Fable.Browser.Gamepad (1.2)
Fable.Core (>= 3.0)
FSharp.Core (>= 4.7.2)
+ Fable.Browser.Geolocation (1.2)
+ Fable.Core (>= 3.0)
+ FSharp.Core (>= 4.7.2)
Fable.Browser.MediaQueryList (1.4)
Fable.Browser.Dom (>= 2.11)
Fable.Browser.Event (>= 1.5)
Fable.Core (>= 3.0)
FSharp.Core (>= 4.7.2)
+ Fable.Browser.MediaStream (3.3)
+ Fable.Browser.Dom (>= 2.11)
+ Fable.Browser.Event (>= 1.5)
+ Fable.Core (>= 3.0)
+ FSharp.Core (>= 4.7.2)
+ Fable.Browser.Navigator (2.0)
+ Fable.Browser.Gamepad (>= 1.0.3)
+ Fable.Browser.Geolocation (>= 1.0.4)
+ Fable.Browser.MediaStream (>= 3.0.4)
+ Fable.Browser.Worker (>= 1.0.5)
+ Fable.Core (>= 3.1.5)
+ FSharp.Core (>= 4.7.2)
Fable.Browser.Svg (2.3)
Fable.Browser.Dom (>= 2.11)
Fable.Core (>= 3.0)
@@ -37,6 +52,10 @@ NUGET
Fable.Browser.Event (>= 1.5)
Fable.Core (>= 3.0)
FSharp.Core (>= 4.7.2)
+ Fable.Browser.Worker (1.2)
+ Fable.Browser.Event (>= 1.5)
+ Fable.Core (>= 3.0)
+ FSharp.Core (>= 4.7.2)
Fable.Core (4.0)
Fable.Elmish (4.0.2)
Fable.Core (>= 3.7.1)
@@ -135,12 +154,12 @@ GITHUB
SourceLink.Create.CommandLine
Unquote
remote: fable-compiler/fable-promise
- FULLPROJECT (28ca5a0b62c9fb94fa9ea861eb8abf6cef59e800)
+ FULLPROJECT (14eca483d664dce5aebbe50fb92a536a7c1ffe53)
remote: alfonsogarciacaro/Feliz.Engine
FULLPROJECT (61152aebd6c0b7878bfc4db05e2e10724b434a3e)
remote: alfonsogarciacaro/Feliz.Snabbdom
FULLPROJECT (7b28ccbceebcc9b66a8cb67aab89b7ae9576dbfb)
remote: davedawkins/Feliz.Engine.Bulma
- FULLPROJECT (9504b9d5a43f5c3ef84086815076ebed1948b162)
+ FULLPROJECT (0df54855ac3464a432cd207412ab66e650a87d77)
remote: davedawkins/Sutil
- FULLPROJECT (cba867e22cb7862d64bf363acf81357c1ffc384d)
\ No newline at end of file
+ FULLPROJECT (343bc31867127638a79afede0dcaa97ca56fa264)
\ No newline at end of file
diff --git a/src/App/App.fsproj b/src/App/App.fsproj
index c6435498..75abc262 100644
--- a/src/App/App.fsproj
+++ b/src/App/App.fsproj
@@ -8,6 +8,7 @@
+
diff --git a/src/App/ConsolePanel.fs b/src/App/ConsolePanel.fs
index 6b160f46..a27961f6 100644
--- a/src/App/ConsolePanel.fs
+++ b/src/App/ConsolePanel.fs
@@ -52,7 +52,7 @@ let renderShowSeparator =
prop.style [
style.justifyContent.center
]
- prop.text "Iframe loaded"
+ prop.text Translations.msg_iframe_loaded
]
let renderBody (isExpanded : bool) (logs : Log list) (setConsoleEnd : HTMLElement -> unit) onContainerScroll =
@@ -134,7 +134,7 @@ let consolePanel =
Html.div [
prop.className "scrollable-panel-header-title"
- prop.text "Console"
+ prop.text Translations.win_header_console
]
Html.div [
diff --git a/src/App/Helpers.fs b/src/App/Helpers.fs
index df15c663..540a51e8 100644
--- a/src/App/Helpers.fs
+++ b/src/App/Helpers.fs
@@ -3,66 +3,117 @@ module Helpers
open Feliz
-type Html with
- static member inline ofOption (element : ReactElement option) : ReactElement =
+type Html with
+
+ static member inline ofOption(element: ReactElement option) : ReactElement =
match element with
- | Some element ->
- element
-
- | None ->
- Html.none
+ | Some element -> element
+
+ | None -> Html.none
module Tooltip =
open System.Text.RegularExpressions
let private stringReplacePatterns =
- [ "<", "<"
- ">", ">"
- """, "\""
- "'", "'"
- "&", "&"
- "", "**Description**\n\n"
- "", "\n"
- "", "\n"
- "", "\n"
- "", ""
- "", "\n" ]
+ [
+ "<", "<"
+ ">", ">"
+ """, "\""
+ "'", "'"
+ "&", "&"
+ "", "**Description**\n\n"
+ "", "\n"
+ "", "\n"
+ "", "\n"
+ "", ""
+ "", "\n"
+ ]
let private regexReplacePatterns =
let r pat = Regex(pat, RegexOptions.IgnoreCase)
- let code (strings : string array) =
+
+ let code (strings: string array) =
let str = strings.[0]
+
if str.Contains("\n") then
- "```forceNoHighlight" + str + "```"
+ "```forceNoHighlight"
+ + str
+ + "```"
else
- "`" + str + "`"
- let returns = Array.item 0 >> sprintf "\n**Returns**\n\n%s"
- let param (s: string[]) = sprintf "* `%s`: %s"(s.[0].Substring(1, s.[0].Length - 2)) s.[1]
- [ r"((?:(?!)(?!<\/c>)[\s\S])*)<\/c>", code
- r"""((?:(?!<\/see>)[\s\S])*)<\/see>""", code
- r"""((?:(?!<\/param>)[\s\S])*)<\/param>""", param
- r"""((?:(?!<\/typeparam>)[\s\S])*)<\/typeparam>""", param
- r"""((?:(?!<\/exception>)[\s\S])*)<\/exception>""", param
- r"""((?:(?!<\/a>)[\s\S])*)<\/a>""", fun s -> (s.[0].Substring(1, s.[0].Length - 2))
-
- r"((?:(?!<\/returns>)[\s\S])*)<\/returns>", returns
+ "`"
+ + str
+ + "`"
+
+ let returns =
+ Array.item 0
+ >> sprintf "\n**Returns**\n\n%s"
+
+ let param (s: string[]) =
+ sprintf
+ "* `%s`: %s"
+ (s.[0]
+ .Substring(
+ 1,
+ s.[0].Length
+ - 2
+ ))
+ s.[1]
+
+ [
+ r "((?:(?!)(?!<\/c>)[\s\S])*)<\/c>", code
+ r
+ """((?:(?!<\/see>)[\s\S])*)<\/see>""",
+ code
+ r
+ """((?:(?!<\/param>)[\s\S])*)<\/param>""",
+ param
+ r
+ """((?:(?!<\/typeparam>)[\s\S])*)<\/typeparam>""",
+ param
+ r
+ """((?:(?!<\/exception>)[\s\S])*)<\/exception>""",
+ param
+ r """((?:(?!<\/a>)[\s\S])*)<\/a>""",
+ fun s ->
+ (s.[0]
+ .Substring(
+ 1,
+ s.[0].Length
+ - 2
+ ))
+
+ r "((?:(?!<\/returns>)[\s\S])*)<\/returns>", returns
]
/// Helpers to create a new section in the markdown comment
- let private suffixXmlKey (tag : string) (value : string) (str : string) =
+ let private suffixXmlKey (tag: string) (value: string) (str: string) =
match str.IndexOf(tag) with
- | x when x <> -1 ->
+ | x when
+ x
+ <> -1
+ ->
let insertAt =
- if str.Chars(x - 1) = ' ' then
- x - 1
+ if
+ str.Chars(
+ x
+ - 1
+ ) = ' '
+ then
+ x
+ - 1
else
x
+
str.Insert(insertAt, value)
| _ -> str
- let private suffixTypeparam = suffixXmlKey " List.fold (fun res (regex: Regex, formatter: string[] -> string) ->
- // repeat replacing with same pattern to handle nested tags, like `......`
- let rec loop res : string =
- match regex.Match res with
- | m when m.Success ->
- m.Groups
- |> Seq.cast
- |> Seq.map (fun g -> g.Value)
- |> Seq.toArray
- |> Array.splitAt 1
- |> function
- | [| firstGroup |], otherGroups ->
- loop <| res.Replace(firstGroup, formatter otherGroups)
+ |> List.fold
+ (fun res (regex: Regex, formatter: string[] -> string) ->
+ // repeat replacing with same pattern to handle nested tags, like `......`
+ let rec loop res : string =
+ match regex.Match res with
+ | m when m.Success ->
+ m.Groups
+ |> Seq.cast
+ |> Seq.map (fun g -> g.Value)
+ |> Seq.toArray
+ |> Array.splitAt 1
+ |> function
+ | [| firstGroup |], otherGroups ->
+ loop
+ <| res.Replace(
+ firstGroup,
+ formatter otherGroups
+ )
+ | _ -> res
| _ -> res
- | _ -> res
- loop res
- ) str
+
+ loop res
+ )
+ str
stringReplacePatterns
- |> List.fold (fun (res: string) (oldValue, newValue) ->
- res.Replace(oldValue, newValue)
- ) res
+ |> List.fold
+ (fun (res: string) (oldValue, newValue) ->
+ res.Replace(oldValue, newValue)
+ )
+ res
diff --git a/src/App/Loader.fs b/src/App/Loader.fs
index 4d2b099e..c44cb466 100644
--- a/src/App/Loader.fs
+++ b/src/App/Loader.fs
@@ -66,7 +66,7 @@ let init (result: Option) =
let private view (model: Model) dispatch =
match model with
| Initializing ->
- Html.text "Initializing"
+ Html.text Translations.msg_initializing
| Running model ->
Main.view model (MainMsg >> dispatch)
@@ -91,19 +91,19 @@ let private view (model: Model) dispatch =
Bulma.title.h3 [
text.hasTextCentered
- prop.text "Fable REPL"
+ prop.text Translations.msg_repl_name
]
Bulma.subtitle.h5 [
text.hasTextCentered
- prop.text "For best experience we recommend running the REPL on a desktop"
+ prop.text Translations.msg_desktop_experience
]
Bulma.level [
Bulma.levelItem [
Bulma.button.a [
prop.onClick (fun _ -> Initialize p |> dispatch)
- prop.text "Continue"
+ prop.text Translations.btn_continue
]
]
]
diff --git a/src/App/Main.fs b/src/App/Main.fs
index 7a33c512..e0f503ef 100644
--- a/src/App/Main.fs
+++ b/src/App/Main.fs
@@ -171,7 +171,7 @@ let private postToGist =
let data =
Encode.object [
"public", Encode.bool true
- "description", Encode.string "Created with Fable REPL"
+ "description", Encode.string Translations.msg_gist_description
"files", Encode.object [
yield "fable-repl.fs", toContent code
yield "fable-repl.html", toContent html
@@ -218,7 +218,7 @@ let private loadGist =
let private showGlobalErrorToast msg =
Toast.message msg
- |> Toast.title "Failed to compile"
+ |> Toast.title Translations.msg_compilation_failed
|> Toast.position Toast.BottomRight
|> Toast.icon Fa.Solid.Exclamation
|> Toast.noTimeout
@@ -271,7 +271,7 @@ let update msg (model : Model) =
]
| LoadFail ->
- let msg = "Assemblies couldn't be loaded. Some firewalls prevent download of binary files, please check."
+ let msg = Translations.msg_assemblies_load_failed
{ model with
State = Idle
}
@@ -337,14 +337,14 @@ let update msg (model : Model) =
JS.console.error exn
model
- , Toast.message "An error occured when creating the gist. Is the token valid?"
+ , Toast.message Translations.msg_gist_token_invalid
|> Toast.icon Fa.Solid.ExclamationTriangle
|> Toast.position Toast.BottomRight
|> Toast.warning
| NoToken ->
model
- , Toast.message "You need to register your GitHub API token before sharing to Gist"
+ , Toast.message Translations.msg_gist_token_missing
|> Toast.icon Fa.Solid.ExclamationTriangle
|> Toast.position Toast.BottomRight
|> Toast.warning
@@ -376,7 +376,7 @@ let update msg (model : Model) =
| Some code -> code
| None -> model.FSharpCode
let opts = model.Sidebar.Options
- let language = model.Sidebar.Options.Language
+ let language = model.Sidebar.Options.Target
CompileCode(code, language, opts.ToOtherFSharpOptions) |> model.Worker.Post
{ model with
@@ -390,7 +390,7 @@ let update msg (model : Model) =
let hasCriticalErrors = errors |> Array.exists (fun e -> not e.IsWarning)
if hasCriticalErrors then
let toastCmd =
- Toast.message "Failed to compile"
+ Toast.message Translations.msg_compilation_failed
|> Toast.position Toast.BottomRight
|> Toast.icon Fa.Solid.Exclamation
|> Toast.dismissOnClick
@@ -410,7 +410,7 @@ let update msg (model : Model) =
| _ -> [fun _ -> saveModel model]
let cmd2 =
- Toast.message "Compiled successfuly"
+ Toast.message Translations.msg_compilation_successful
|> Toast.position Toast.BottomRight
|> Toast.icon Fa.Solid.Check
|> Toast.dismissOnClick
@@ -565,8 +565,8 @@ let update msg (model : Model) =
| ShareableUrlReady () ->
model
- , Toast.message "Copy it from the address bar"
- |> Toast.title "Shareable link is ready"
+ , Toast.message Translations.msg_shareable_url_ready_text
+ |> Toast.title Translations.msg_shareable_url_ready_title
|> Toast.position Toast.BottomRight
|> Toast.icon Fa.Solid.InfoCircle
|> Toast.timeout (System.TimeSpan.FromSeconds 5.)
@@ -575,7 +575,7 @@ let update msg (model : Model) =
| LoadGistError exn ->
JS.console.error exn
model
- , Toast.message "An error occured when loading the gist"
+ , Toast.message Translations.msg_load_gist_error
|> Toast.icon Fa.Solid.ExclamationTriangle
|> Toast.position Toast.BottomRight
|> Toast.warning
@@ -583,7 +583,7 @@ let update msg (model : Model) =
| UpdateQueryFailed exn ->
JS.console.error exn
model
- , Toast.message "An error occured when updating the URL"
+ , Toast.message Translations.msg_update_url_failed
|> Toast.icon Fa.Solid.ExclamationTriangle
|> Toast.position Toast.BottomRight
|> Toast.warning
@@ -757,12 +757,12 @@ let private problemsPanel (isExpanded : bool) (errors : Monaco.Editor.IMarkerDat
let title =
if errors.Length = 0 then
Html.span [
- prop.text "Problems"
+ prop.text Translations.msg_problems
]
else
Html.span [
prop.children [
- Html.text "Problems: "
+ Html.text Translations.msg_problems_info
Html.span [
prop.style [
style.marginLeft (length.em 0.5)
@@ -815,7 +815,7 @@ let private problemsPanel (isExpanded : bool) (errors : Monaco.Editor.IMarkerDat
match error.severity with
| Monaco.MarkerSeverity.Error -> Fa.Solid.TimesCircle, color.isDanger, "is-danger"
| Monaco.MarkerSeverity.Warning -> Fa.Solid.ExclamationTriangle, color.isWarning, "is-warning"
- | _ -> failwith "Should not happen", color.isDanger, ""
+ | _ -> failwith Translations.msg_fatal_error, color.isDanger, ""
Html.div [
prop.className ("scrollable-panel-body-row " + colorClass)
@@ -984,7 +984,7 @@ let private outputTabs (activeTab : OutputTab) dispatch =
)
prop.children [
Html.a [
- prop.text "Live sample"
+ prop.text Translations.msg_live_sample_text
]
]
]
@@ -997,7 +997,7 @@ let private outputTabs (activeTab : OutputTab) dispatch =
)
prop.children [
Html.a [
- prop.text "Code"
+ prop.text Translations.msg_code_text
]
]
]
@@ -1034,7 +1034,7 @@ let private viewCodeEditor (model: Model) dispatch =
editor.isHidden (model.OutputTab <> OutputTab.Code)
editor.customClass (fontSizeClass model.Sidebar.Options.FontSize)
editor.editorDidMount (onJsEditorDidMount model dispatch)
- editor.language (model.Sidebar.Options.Language.ToLower())
+ editor.language (model.Sidebar.Options.Target.ToLower())
]
let private outputArea model dispatch =
diff --git a/src/App/Mouse.fs b/src/App/Mouse.fs
index 0ac959d7..cd0f08a1 100644
--- a/src/App/Mouse.fs
+++ b/src/App/Mouse.fs
@@ -63,7 +63,10 @@ module Cmd =
|> Decode.map args.ConsoleErrorCor
| x ->
// Discard messages we don't know how to handle it
- sprintf "`%A` is not a known value for an iframe message" x
+ let formatMessage =
+ PrintfFormat string,unit,string,string>(Translations.msg_invalid_iframe_error)
+
+ sprintf formatMessage x
|> Decode.fail
)
Decode.fromValue "$" iframeMessageDecoder ev?data
diff --git a/src/App/Sidebar.fs b/src/App/Sidebar.fs
index 6a634683..645c25aa 100644
--- a/src/App/Sidebar.fs
+++ b/src/App/Sidebar.fs
@@ -222,7 +222,7 @@ let private collapseButton dispatch =
prop.onClick (fun _ -> dispatch ToggleState)
prop.children [
Bulma.cardHeader [
- Bulma.cardHeaderTitle.div "Collapse sidebar"
+ Bulma.cardHeaderTitle.div Translations.msg_collapse_sidebar
Bulma.cardHeaderIcon.span [
Bulma.icon [
Fa.i [ Fa.Solid.AngleDoubleLeft; Fa.Size Fa.FaLarge ] [ ]
@@ -242,7 +242,7 @@ let private sidebarContainer dispatch (sections : ReactElement list) =
Html.img [
prop.src "img/fable-ionide.png"
]
- Bulma.title.h4 "Fable REPL"
+ Bulma.title.h4 Translations.msg_repl_name
]
]
@@ -274,11 +274,11 @@ let view (isCompiling : bool) (model: Model) dispatch =
let widgets =
[
if model.IsExpanded then
- "General", Fa.Solid.Th, Widgets.General.viewExpanded isCompiling model.Options.GistToken model.General (GeneralMsg >> dispatch), None
- "Samples", Fa.Solid.Book, Widgets.Samples.view model.Samples (SamplesMsg >> dispatch), Some 500
- "Options", Fa.Solid.Cog, Widgets.Options.view model.Options (OptionsMsg >> dispatch), None
- "Statistics", Fa.Regular.Clock, Widgets.Stats.view model.Statistics, None
- "About", Fa.Solid.Info, Widgets.About.view model.FableVersion, None
+ Translations.msg_widget_general, Fa.Solid.Th, Widgets.General.viewExpanded isCompiling model.Options.GistToken model.General (GeneralMsg >> dispatch), None
+ Translations.msg_widget_samples, Fa.Solid.Book, Widgets.Samples.view model.Samples (SamplesMsg >> dispatch), Some 500
+ Translations.msg_widget_options, Fa.Solid.Cog, Widgets.Options.view model.Options (OptionsMsg >> dispatch), None
+ Translations.msg_widget_statistics, Fa.Regular.Clock, Widgets.Stats.view model.Statistics, None
+ Translations.msg_widget_about, Fa.Solid.Info, Widgets.About.view model.FableVersion, None
]
|> List.map (renderWidgets model dispatch)
diff --git a/src/App/Translations.fs b/src/App/Translations.fs
new file mode 100644
index 00000000..85efebe9
--- /dev/null
+++ b/src/App/Translations.fs
@@ -0,0 +1,289 @@
+[]
+module Translations
+
+open Browser
+open Fable.Core
+
+let enTranslations =
+ {|
+ english = "English"
+ russian = "Russian"
+ ukrainian = "Ukrainian"
+ msg_iframe_loaded = "Iframe loaded"
+ win_header_console = "Console"
+ msg_initializing = "Initializing"
+ msg_repl_name = "Fable REPL"
+ msg_desktop_experience =
+ "For best experience we recommend running the REPL on a desktop"
+ btn_continue = "Continue"
+ msg_gist_description = "Created with Fable REPL"
+ msg_compilation_failed = "Failed to compile"
+ msg_assemblies_load_failed =
+ "Assemblies couldn't be loaded. Some firewalls prevent download of binary files, please check."
+ msg_gist_token_invalid =
+ "An error occurred when creating the gist. Is the token valid?"
+ msg_gist_token_missing =
+ "You need to register your GitHub API token before sharing to Gist"
+ msg_compilation_successful = "Compiled successfully"
+ msg_shareable_url_ready_text = "Copy it from the address bar"
+ msg_shareable_url_ready_title = "Shareable link is ready"
+ msg_load_gist_error = "An error occurred when loading the gist"
+ msg_update_url_failed = "An error occurred when updating the URL"
+ msg_fatal_error = "Should not happen"
+ msg_live_sample_text = "Live sample"
+ msg_code_text = "Code"
+ msg_problems = "Problems"
+ msg_problems_info = "Problems = "
+ msg_invalid_iframe_error =
+ "`%A` is not a known value for an iframe message"
+ msg_collapse_sidebar = "Collapse sidebar"
+ msg_widget_general = "General"
+ msg_widget_samples = "Samples"
+ msg_widget_options = "Options"
+ msg_widget_statistics = "Statistics"
+ msg_widget_about = "About"
+ msg_found_a_bug = "Found a bug ?"
+ msg_general_compile_run_tooltip = "Compile and run (Alt+Enter)"
+ msg_general_compile_run_text = "Compile and run"
+ msg_general_refresh_sample_tooltip =
+ "Refresh the live sample (without compiling)"
+ msg_general_refresh_sample_text = "Refresh the live sample"
+ msg_general_reset_repl_tooltip =
+ "Reset the REPL, you will lose your current work"
+ msg_general_reset_repl_text = "Click here to reset"
+ msg_general_share_url_tooltip = "Share using the URL"
+ msg_general_share_url_text = "Share using the URL"
+ msg_general_share_gist_tooltip = "Share to Gist"
+ msg_general_share_gist_text = "Share to Gist"
+ msg_reset_confirmation_text = "Please, confirm to reset"
+ btn_confirm = "Confirm"
+ btn_cancel = "Cancel"
+ btn_save = "Save"
+ msg_options_editor_font_size = "Editors font size"
+ msg_options_editor_font_family = "Editors font family"
+ msg_options_size_small = "Small"
+ msg_options_size_medium = "Medium"
+ msg_options_size_large = "Large"
+ msg_options_programming_language = "Target"
+ msg_options_interface_language = "Language"
+ msg_options_settings_optimize = "Optimize (experimental)"
+ msg_options_settings_debug = "Define DEBUG"
+ msg_options_settings_typed_arrays = "Typed Arrays"
+ msg_options_gist_token_delete = "Delete gist token"
+ msg_options_gist_token_github_token = "Github token"
+ msg_options_gist_token_github_token_create = " (Create)"
+ msg_options_gist_token_gist_scope = "Token with gist scope"
+ msg_samples_refresh_samples = "Refresh samples"
+ msg_stats_steps = "Steps"
+ msg_stats_milliseconds_short = "ms"
+ |}
+
+let ukTranslations =
+ {|
+ english = "Англійська"
+ russian = "Російський"
+ ukrainian = "Українська"
+ msg_iframe_loaded = "Iframe завантажено"
+ win_header_console = "Консоль"
+ msg_initializing = "Ініціалізуємо"
+ msg_repl_name = "Fable REPL"
+ msg_desktop_experience =
+ "Для найкращого досвіду ми рекомендуємо запускати REPL із десктопа"
+ btn_continue = "Продовжити"
+ msg_gist_description = "Створено із REPL"
+ msg_compilation_failed = "Не вдалося скомпілювати"
+ msg_assemblies_load_failed =
+ "Не вдалося завантажити збірки. Деякі брандмауери запобігають завантаженню бінарних файлів, перевірте."
+ msg_gist_token_invalid =
+ "Під час створення gist-у сталася помилка. Чи токен дійсний?"
+ msg_gist_token_missing =
+ "Вам потрібно зареєструвати свій токен API GitHub перед тим, як надати спільний доступ до Gist"
+ msg_compilation_successful = "Скомпільовано успішно"
+ msg_shareable_url_ready_text = "Скопіюйте його з адресного рядка"
+ msg_shareable_url_ready_title =
+ "Посилання для спільного доступу готове"
+ msg_load_gist_error = "Під час завантаження gist-у сталася помилка"
+ msg_update_url_failed =
+ "Під час оновлення URL-адреси сталася помилка"
+ msg_fatal_error = "Не повинно траплятися"
+ msg_live_sample_text = "Інтерактивний приклад"
+ msg_code_text = "Код"
+ msg_problems = "Проблеми"
+ msg_problems_info = "Проблеми = "
+ msg_invalid_iframe_error =
+ "Невідоме значення `%A` для повідомлення від iframe"
+ msg_collapse_sidebar = "Згорнути бічну панель"
+ msg_widget_general = "Загальний"
+ msg_widget_samples = "Приклади"
+ msg_widget_options = "Опції"
+ msg_widget_statistics = "Статистика"
+ msg_widget_about = "О REPL"
+ msg_found_a_bug = "Знайшов помилку ?"
+ msg_general_compile_run_tooltip =
+ "Скомпілювати та запустити (Alt+Enter)"
+ msg_general_compile_run_text = "Скомпілювати та запустити"
+ msg_general_refresh_sample_tooltip =
+ "Оновити інтерактивний приклад (без компіляції)"
+ msg_general_refresh_sample_text = "Оновити інтерактивний приклад"
+ msg_general_reset_repl_tooltip =
+ "Скинувши REPL, ви втратите поточну роботу"
+ msg_general_reset_repl_text =
+ "Скинувши REPL, ви втратите поточну роботу"
+ msg_general_share_url_tooltip = "Поділіться за допомогою URL-адреси"
+ msg_general_share_url_text = "Поділіться за допомогою URL-адреси"
+ msg_general_share_gist_tooltip = "Поділитися через Gist"
+ msg_general_share_gist_text = "Поділитися через Gist"
+ msg_reset_confirmation_text = "Підтвердьте, щоб скинути"
+ btn_confirm = "Підтвердити"
+ btn_cancel = "Скасувати"
+ btn_save = "Зберегти"
+ msg_options_editor_font_size = "Розмір шрифта редактора"
+ msg_options_editor_font_family = "Сімейство шрифта редактора"
+ msg_options_size_small = "Малий"
+ msg_options_size_medium = "Середній"
+ msg_options_size_large = "Великий"
+ msg_options_interface_language = "Мова"
+ msg_options_programming_language = "Цільова"
+ msg_options_settings_optimize = "Оптимизувати (експеріментально)"
+ msg_options_settings_debug = "Визначити DEBUG"
+ msg_options_settings_typed_arrays = "Типізовані масиви"
+ msg_options_gist_token_delete = "Видалити токен gist"
+ msg_options_gist_token_github_token = "Github токен"
+ msg_options_gist_token_github_token_create = " (Створити)"
+ msg_options_gist_token_gist_scope = "Токен із gist скоупом"
+ msg_samples_refresh_samples = "Оновити приклади"
+ msg_stats_steps = "Кроки"
+ msg_stats_milliseconds_short = "мс"
+ |}
+
+let ruTranslations =
+ {|
+ english = "Английский"
+ russian = "Русский"
+ ukrainian = "Украинец"
+ msg_iframe_loaded = "Iframe загружен"
+ win_header_console = "Консоль"
+ msg_initializing = "Инициализируем"
+ msg_repl_name = "Fable REPL"
+ msg_desktop_experience =
+ "Для наилучшего опыта мы рекомендуем запускать REPL с десктопа"
+ btn_continue = "Продолжить"
+ msg_gist_description = "Создано из REPL"
+ msg_compilation_failed = "Не удалось скомпилировать"
+ msg_assemblies_load_failed =
+ "Не удалось скачать зборки. Некоторые брэндмауэры предотвращают загрузку бинарных файлов, проверьте."
+ msg_gist_token_invalid =
+ "Во время создания gist-а произошла ошибка. Токен действителен?"
+ msg_gist_token_missing =
+ "Вам необходимо зарегистрировать свой токен API GitHub перед тим, как дать совместный доступ к Gist"
+ msg_compilation_successful = "Скомпилировано успешно"
+ msg_shareable_url_ready_text = "Скопируйте ее и адресной строки"
+ msg_shareable_url_ready_title =
+ "Ссылка для совместного доступа готова"
+ msg_load_gist_error = "Во время загрузки gist-а произошла ошибка"
+ msg_update_url_failed =
+ "Во время обновления URL-адреса произошла ошибка"
+ msg_fatal_error = "Не должно случаться"
+ msg_live_sample_text = "Интерактивний пример"
+ msg_code_text = "Код"
+ msg_problems = "Проблемы"
+ msg_problems_info = "Проблемы = "
+ msg_invalid_iframe_error =
+ "Неизвестное значення `%A` для сообщения от iframe"
+ msg_collapse_sidebar = "Свернуть боковую панель"
+ msg_widget_general = "Общее"
+ msg_widget_samples = "Примеры"
+ msg_widget_options = "Опции"
+ msg_widget_statistics = "Статистика"
+ msg_widget_about = "О REPL"
+ msg_found_a_bug = "Нашел ошибку?"
+ msg_general_compile_run_tooltip =
+ "Скомпилировать и запустить (Alt+Enter)"
+ msg_general_compile_run_text = "Скомпилировать и запустить"
+ msg_general_refresh_sample_tooltip =
+ "Оновить интерактивний пример (без компиляции)"
+ msg_general_refresh_sample_text = "Оновить интерактивний пример"
+ msg_general_reset_repl_tooltip =
+ "Сбрасывая REPL, ви потеряете текущую работу"
+ msg_general_reset_repl_text =
+ "Сбрасывая REPL, ви потеряете текущую работу"
+ msg_general_share_url_tooltip = "Поделится с помощью URL-адреса"
+ msg_general_share_url_text = "Поделится с помощью URL-адреса"
+ msg_general_share_gist_tooltip = "Поделится через Gist"
+ msg_general_share_gist_text = "Поделится через Gist"
+ msg_reset_confirmation_text = "Подтвердите, чтобы сбросить"
+ btn_confirm = "Подтвердить"
+ btn_cancel = "Отменить"
+ btn_save = "Сохранить"
+ msg_options_editor_font_size = "Размер шрифта редактора"
+ msg_options_editor_font_family = "Семейство шрифта редактора"
+ msg_options_size_small = "Маленький"
+ msg_options_size_medium = "Средний"
+ msg_options_size_large = "Большой"
+ msg_options_programming_language = "Язык"
+ msg_options_interface_language = "Язык"
+ msg_options_settings_optimize = "Оптимизировать (экспериментально)"
+ msg_options_settings_debug = "Определить DEBUG"
+ msg_options_settings_typed_arrays = "Типизированные массивы"
+ msg_options_gist_token_delete = "Удалить токен gist"
+ msg_options_gist_token_github_token = "Github токен"
+ msg_options_gist_token_github_token_create = " (Создать)"
+ msg_options_gist_token_gist_scope = "Токен с gist скоупом"
+ msg_samples_refresh_samples = "Обновить примеры"
+ msg_stats_steps = "Шаги"
+ msg_stats_milliseconds_short = "мс"
+ |}
+
+[]
+[]
+type LanguageCode =
+ | [] English
+ | [] Ukrainian
+ | [] Russian
+
+module LanguageCode =
+
+ let isValidText (code : string) =
+ match code with
+ | "en" | "uk" | "ru" -> true
+ | _ -> false
+
+ let fromText (code : string) =
+ match code with
+ | "en" -> LanguageCode.English
+ | "uk" -> LanguageCode.Ukrainian
+ | "ru" -> LanguageCode.Russian
+ | _ -> LanguageCode.English
+
+let translations =
+ dict
+ [
+ LanguageCode.English, enTranslations
+ LanguageCode.Ukrainian, ukTranslations
+ LanguageCode.Russian, ruTranslations
+ ]
+
+[]
+let LOCAL_STORAGE_KEY = "fable_repl_language"
+
+let activeLanguageCode =
+ let storedLanguageCode = localStorage.[LOCAL_STORAGE_KEY]
+ // Try to get the language from the local storage in case the user
+ // changed it in the settings
+ if storedLanguageCode <> null && LanguageCode.isValidText storedLanguageCode then
+ LanguageCode.fromText storedLanguageCode
+ else
+ navigator.language
+ |> Option.map (fun lang ->
+ let languageCode = lang.Substring(0, 2)
+
+ LanguageCode.fromText languageCode
+ )
+ |> Option.defaultValue LanguageCode.English
+
+let storedLanguageCode (code : LanguageCode) =
+ // Code is a string enum, so it is already a string at runtime
+ localStorage.[LOCAL_STORAGE_KEY] <- unbox code
+
+let Translations =
+ translations[activeLanguageCode]
diff --git a/src/App/Widgets/About.fs b/src/App/Widgets/About.fs
index 50e52ef7..f667425f 100644
--- a/src/App/Widgets/About.fs
+++ b/src/App/Widgets/About.fs
@@ -17,7 +17,7 @@ let view fableVersion =
prop.style [
style.textDecoration.underline
]
- prop.text "Found a bug ?"
+ prop.text Translations.msg_found_a_bug
]
]
]
diff --git a/src/App/Widgets/General.fs b/src/App/Widgets/General.fs
index f3edae47..1f082d9c 100644
--- a/src/App/Widgets/General.fs
+++ b/src/App/Widgets/General.fs
@@ -99,13 +99,13 @@ let viewCollapsed (isCompiling : bool) (gistToken : string option) (model: Model
prop.className "actions-area"
prop.children [
- actionButton "Compile and run (Alt+Enter)" StartCompile compileIcon
- actionButton "Refresh the live sample (without compiling)" RefreshIframe [ Fa.i [ Fa.Solid.Redo ] [ ] ]
- actionButton "Reset the REPL, you will lose your current work" AskReset [ Fa.i [ Fa.Solid.TrashAlt ] [ ] ]
- actionButton "Share using the URL" Share [ Fa.i [ Fa.Solid.Share ] [ ] ]
+ actionButton Translations.msg_general_compile_run_tooltip StartCompile compileIcon
+ actionButton Translations.msg_general_refresh_sample_tooltip RefreshIframe [ Fa.i [ Fa.Solid.Redo ] [ ] ]
+ actionButton Translations.msg_general_reset_repl_tooltip AskReset [ Fa.i [ Fa.Solid.TrashAlt ] [ ] ]
+ actionButton Translations.msg_general_share_url_tooltip Share [ Fa.i [ Fa.Solid.Share ] [ ] ]
match gistToken with
| Some _ ->
- actionButton "Share to Gist" ShareToGist [ Fa.i [ Fa.Brand.Github ] [ ] ]
+ actionButton Translations.msg_general_share_gist_tooltip ShareToGist [ Fa.i [ Fa.Brand.Github ] [ ] ]
| None -> Html.none
]
@@ -150,13 +150,13 @@ let viewExpanded (isCompiling : bool) (gistToken : string option) (model: Model)
match model.ResetState with
| Default ->
Html.div [
- renderItem "Compile and run" isCompiling StartCompile Fa.Solid.Play
- renderItem "Refresh the live sample" isCompiling RefreshIframe Fa.Solid.Redo
- renderItem "Click here to reset" false AskReset Fa.Solid.TrashAlt
- renderItem "Share using the URL" false Share Fa.Solid.Share
+ renderItem Translations.msg_general_compile_run_text isCompiling StartCompile Fa.Solid.Play
+ renderItem Translations.msg_general_refresh_sample_text isCompiling RefreshIframe Fa.Solid.Redo
+ renderItem Translations.msg_general_reset_repl_text false AskReset Fa.Solid.TrashAlt
+ renderItem Translations.msg_general_share_url_text false Share Fa.Solid.Share
match gistToken with
| Some _ ->
- renderItem "Share to Gist" false ShareToGist Fa.Brand.Github
+ renderItem Translations.msg_general_share_gist_text false ShareToGist Fa.Brand.Github
| None ->
Html.none
]
@@ -165,7 +165,7 @@ let viewExpanded (isCompiling : bool) (gistToken : string option) (model: Model)
Bulma.field.div [
Bulma.help [
color.isWarning
- prop.text "Please, confirm to reset"
+ prop.text Translations.msg_reset_confirmation_text
]
Bulma.field.div [
@@ -179,7 +179,7 @@ let viewExpanded (isCompiling : bool) (gistToken : string option) (model: Model)
Bulma.icon [
Fa.i [ Fa.Solid.Check ] [ ]
]
- Html.span "Confirm"
+ Html.span Translations.btn_confirm
]
]
]
@@ -192,7 +192,7 @@ let viewExpanded (isCompiling : bool) (gistToken : string option) (model: Model)
Bulma.icon [
Fa.i [ Fa.Solid.Times ] [ ]
]
- Html.span "Cancel"
+ Html.span Translations.btn_cancel
]
]
]
@@ -223,7 +223,7 @@ let viewModalResetConfirmation (model: Model) dispatch =
prop.children [
Html.span [
prop.className "reset-confirmation-modal-content-text"
- prop.text "Please, confirm to reset"
+ prop.text Translations.msg_reset_confirmation_text
]
Html.div [
@@ -243,7 +243,7 @@ let viewModalResetConfirmation (model: Model) dispatch =
Fa.i [ Fa.Solid.Check ] [ ]
]
- Html.span "Confirm"
+ Html.span Translations.btn_confirm
]
]
]
@@ -257,7 +257,7 @@ let viewModalResetConfirmation (model: Model) dispatch =
Fa.i [ Fa.Solid.Times ] [ ]
]
- Html.span "Cancel"
+ Html.span Translations.btn_cancel
]
]
]
diff --git a/src/App/Widgets/Options.fs b/src/App/Widgets/Options.fs
index 4587581d..c9810928 100644
--- a/src/App/Widgets/Options.fs
+++ b/src/App/Widgets/Options.fs
@@ -18,7 +18,7 @@ type Model =
{ Optimize : bool
DefineDebug : bool
TypedArrays : bool
- Language : string
+ Target : string
FontSize : float
FontFamily : string
GistToken : string option
@@ -33,7 +33,7 @@ type Model =
{ Optimize = false
DefineDebug = true
TypedArrays = true
- Language = "javascript"
+ Target = "javascript"
FontSize = 14.
FontFamily = MONACO_DEFAULT_FONT_FAMILY
GistToken = None
@@ -47,7 +47,7 @@ type Model =
// |> Option.defaultValue false
DefineDebug = get.Optional.Field "defineDebug" Decode.bool |> Option.defaultValue true
TypedArrays = get.Optional.Field "typedArrays" Decode.bool |> Option.defaultValue true
- Language = get.Optional.Field "language" Decode.string |> Option.defaultValue "javascript"
+ Target = get.Optional.Field "language" Decode.string |> Option.defaultValue "javascript"
FontSize = get.Optional.Field "fontSize" Decode.float
|> Option.defaultValue 14.
FontFamily = get.Optional.Field "fontFamily" Decode.string
@@ -61,7 +61,7 @@ type Model =
[ yield "optimize", Encode.bool model.Optimize
yield "defineDebug", Encode.bool model.DefineDebug
yield "typedArrays", Encode.bool model.TypedArrays
- yield "language", Encode.string model.Language
+ yield "language", Encode.string model.Target
yield "fontSize", Encode.float model.FontSize
yield "fontFamily", Encode.string model.FontFamily
match model.GistToken with
@@ -117,7 +117,7 @@ let update msg model =
{ model with TypedArrays = not model.TypedArrays }, ExtMsg.NoOp
| ChangeLanguage newLang ->
- { model with Language = newLang }, ExtMsg.Recompile
+ { model with Target = newLang }, ExtMsg.Recompile
| ChangeFontSize newSize ->
{ model with FontSize = newSize }, ExtMsg.NoOp
@@ -136,7 +136,7 @@ let update msg model =
{ model with GistToken = None}, ExtMsg.NoOp
// Save the setting in localStorage
- // Like that they are persistant between REPL reload
+ // Like that they are persistent between REPL reload
|> saveSettings
let private fontSizeOption (label : string) (fontSize : float) =
@@ -147,7 +147,7 @@ let private fontSizeOption (label : string) (fontSize : float) =
let inline private fontSizeSetting (fontSize : float) dispatch =
Bulma.field.div [
- Bulma.label "Editors font size"
+ Bulma.label Translations.msg_options_editor_font_size
Bulma.control.div [
Bulma.select [
@@ -157,9 +157,9 @@ let inline private fontSizeSetting (fontSize : float) dispatch =
ev.Value |> float |> ChangeFontSize |> dispatch
)
prop.children [
- fontSizeOption "Small" 12.
- fontSizeOption "Medium" 14.
- fontSizeOption "Large" 16.
+ fontSizeOption Translations.msg_options_size_small 12.
+ fontSizeOption Translations.msg_options_size_medium 14.
+ fontSizeOption Translations.msg_options_size_large 16.
]
]
]
@@ -173,7 +173,7 @@ let private fontFamilyOption (label : string) (fontFamily : string) =
let inline private fontFamilySetting (fontFamily : string) dispatch =
Bulma.field.div [
- Bulma.label "Editors font family"
+ Bulma.label Translations.msg_options_editor_font_family
Bulma.control.div [
Bulma.select [
@@ -190,7 +190,7 @@ let inline private fontFamilySetting (fontFamily : string) dispatch =
]
]
-let private languageSetting (language : string) dispatch =
+let private targetSetting (target : string) dispatch =
let option (txt: string) =
Html.option [
prop.value txt
@@ -198,12 +198,12 @@ let private languageSetting (language : string) dispatch =
]
Bulma.field.div [
- Bulma.label "Language"
+ Bulma.label Translations.msg_options_programming_language
Bulma.control.div [
Bulma.select [
select.isFullWidth
- prop.value language
+ prop.value target
prop.onChange (fun (ev : Types.Event) ->
ev.Value |> ChangeLanguage |> dispatch
)
@@ -218,6 +218,34 @@ let private languageSetting (language : string) dispatch =
]
]
+let private languageSetting=
+ let option (code : LanguageCode) (txt: string) =
+ Html.option [
+ prop.value (unbox code)
+ prop.text txt
+ ]
+
+ Bulma.field.div [
+ Bulma.label Translations.msg_options_interface_language
+
+ Bulma.control.div [
+ Bulma.select [
+ select.isFullWidth
+ prop.value (unbox activeLanguageCode)
+ prop.onChange (fun (ev : Types.Event) ->
+ LanguageCode.fromText ev.Value |> storedLanguageCode
+ // Naive way to refresh the page and update the translations
+ window.location.reload()
+ )
+ prop.children [
+ option LanguageCode.English Translations.english
+ option LanguageCode.Russian Translations.russian
+ option LanguageCode.Ukrainian Translations.ukrainian
+ ]
+ ]
+ ]
+ ]
+
let private switchOption (label : string) isActive dispatch msg =
Bulma.field.div [
Bulma.control.div [
@@ -240,13 +268,13 @@ let private switchOption (label : string) isActive dispatch msg =
]
let inline private optimizeSetting (model: Model) dispatch =
- switchOption "Optimize (experimental)" model.Optimize dispatch ToggleOptimize
+ switchOption Translations.msg_options_settings_optimize model.Optimize dispatch ToggleOptimize
let private defineDebugSetting (model: Model) dispatch =
- switchOption "Define DEBUG" model.DefineDebug dispatch ToggleDefineDebug
+ switchOption Translations.msg_options_settings_debug model.DefineDebug dispatch ToggleDefineDebug
let private typedArraysSetting (model: Model) dispatch =
- switchOption "Typed Arrays" model.TypedArrays dispatch ToggleTypedArrays
+ switchOption Translations.msg_options_settings_typed_arrays model.TypedArrays dispatch ToggleTypedArrays
let inline private gistTokenSetting (token : string option) (tokenField : string) dispatch =
match token with
@@ -255,7 +283,7 @@ let inline private gistTokenSetting (token : string option) (tokenField : string
Bulma.button.a [
prop.onClick (fun _ -> dispatch DeleteToken)
button.isFullWidth
- prop.text "Delete gist token"
+ prop.text Translations.msg_options_gist_token_delete
]
]
@@ -263,11 +291,11 @@ let inline private gistTokenSetting (token : string option) (tokenField : string
Bulma.field.div [
Bulma.label [
prop.children [
- Html.text "Github token"
+ Html.text Translations.msg_options_gist_token_github_token
Html.a [
prop.target "_blank"
prop.href "https://github.com/settings/tokens/new?description=fable-repl&scopes=gist"
- prop.text " (Create)"
+ prop.text Translations.msg_options_gist_token_github_token_create
]
]
]
@@ -277,13 +305,13 @@ let inline private gistTokenSetting (token : string option) (tokenField : string
prop.children [
Bulma.input.password [
prop.onChange (fun (ev : Types.Event) -> ev.Value |> ChangeGistToken |> dispatch)
- prop.placeholder "Token with gist scope"
+ prop.placeholder Translations.msg_options_gist_token_gist_scope
]
if tokenField.Length = 40 then
Bulma.button.a [
prop.onClick (fun _ -> dispatch SaveToken)
- prop.text "Save"
+ prop.text Translations.btn_save
]
]
]
@@ -297,10 +325,11 @@ let view (model: Model) dispatch =
prop.children [
defineDebugSetting model dispatch
typedArraysSetting model dispatch
- languageSetting model.Language dispatch
+ targetSetting model.Target dispatch
fontFamilySetting model.FontFamily dispatch
fontSizeSetting model.FontSize dispatch
gistTokenSetting model.GistToken model.GistTokenField dispatch
+ languageSetting
// TODO: Optimize is disabled to prevent problems with inline functions in REPL Lib
// optimizeSetting model dispatch
]
diff --git a/src/App/Widgets/Samples.fs b/src/App/Widgets/Samples.fs
index 6679e729..1e62b10c 100644
--- a/src/App/Widgets/Samples.fs
+++ b/src/App/Widgets/Samples.fs
@@ -79,8 +79,8 @@ and MenuType =
| "menu-item" ->
MenuType.DecodeMenuItem
- | unkown ->
- sprintf "Unkown type `%s` for the sample" unkown
+ | unknown ->
+ sprintf "Unknown type `%s` for the sample" unknown
|> Decode.fail
)
@@ -301,7 +301,7 @@ let view model dispatch =
prop.onClick fetchSamplesMsg
prop.children [
- Html.span "Refresh samples"
+ Html.span Translations.msg_samples_refresh_samples
]
]
]
@@ -315,4 +315,3 @@ let view model dispatch =
#endif
Bulma.menu menus
-
\ No newline at end of file
diff --git a/src/App/Widgets/Stats.fs b/src/App/Widgets/Stats.fs
index 264a8efd..9395b034 100644
--- a/src/App/Widgets/Stats.fs
+++ b/src/App/Widgets/Stats.fs
@@ -26,10 +26,10 @@ let view (model : Model) =
Bulma.table [
Html.thead [
Html.tr [
- Html.th "Steps"
+ Html.th Translations.msg_stats_steps
Html.th [
prop.className "has-text-right"
- prop.text "ms"
+ prop.text Translations.msg_stats_milliseconds_short
]
]
]
diff --git a/src/App/paket.references b/src/App/paket.references
index f3bf4f70..02c8b5f2 100644
--- a/src/App/paket.references
+++ b/src/App/paket.references
@@ -9,4 +9,5 @@ Fable.Fetch
Fable.FontAwesome.Free
Feliz
Feliz.Bulma
-Feliz.Bulma.Tooltip
\ No newline at end of file
+Feliz.Bulma.Tooltip
+Fable.Browser.Navigator
\ No newline at end of file
diff --git a/src/App/public/samples/Samples.fsproj b/src/App/public/samples/Samples.fsproj
index 195f49bd..12bf1973 100644
--- a/src/App/public/samples/Samples.fsproj
+++ b/src/App/public/samples/Samples.fsproj
@@ -72,4 +72,5 @@
-
+
+
\ No newline at end of file
diff --git a/src/App/public/samples/elmish/thoth_random_user.fs b/src/App/public/samples/elmish/thoth_random_user.fs
index a3888720..1068e4d9 100644
--- a/src/App/public/samples/elmish/thoth_random_user.fs
+++ b/src/App/public/samples/elmish/thoth_random_user.fs
@@ -109,7 +109,7 @@ let update (msg:Msg) (model:Model) =
JS.console.error msg
Errored, Cmd.none
- // An error occured, when fetching the new user
+ // An error occurred, when fetching the new user
| FetchError error ->
JS.console.error error.Message
Errored, Cmd.none
@@ -134,7 +134,7 @@ let private viewLoading =
viewMessage "is-info" "Waiting the server response..."
let private viewErrored =
- viewMessage "is-danger" "An error occured, please check the console for more information."
+ viewMessage "is-danger" "An error occurred, please check the console for more information."
let private viewUser (user : User) =
let birthday =
@@ -198,4 +198,4 @@ let view model dispatch =
// App
Program.mkProgram init update view
|> Program.withReactSynchronous "elmish-app"
-|> Program.run
\ No newline at end of file
+|> Program.run
diff --git a/src/App/public/samples/elmish/validation.fs b/src/App/public/samples/elmish/validation.fs
index c1c71ad5..780125e9 100644
--- a/src/App/public/samples/elmish/validation.fs
+++ b/src/App/public/samples/elmish/validation.fs
@@ -57,7 +57,7 @@ module Http =
Cmd.OfAsync.either loginAsync info
successHandler
- (fun ex -> LoginFailed "Unknown error occured while logging you in")
+ (fun ex -> LoginFailed "Unknown error occurred while logging you in")
let init() =
@@ -215,4 +215,4 @@ let render (state: State) dispatch =
Program.mkProgram init update render
|> Program.withReactSynchronous "elmish-app"
-|> Program.run
\ No newline at end of file
+|> Program.run