Skip to content

Commit 2820cb0

Browse files
Alex619829smira
authored andcommitted
feat(i18n): frontend localization support
Adds localization support with go-i18n module. Adds Russian language support in the frontend interface Signed-off-by: Aleksandr Gamzin <gamzin@altlinux.org> Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
1 parent f1187bc commit 2820cb0

21 files changed

Lines changed: 907 additions & 121 deletions

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/h2non/filetype v1.1.3
99
github.com/julienschmidt/httprouter v1.3.0
1010
github.com/klauspost/compress v1.18.0
11+
github.com/nicksnyder/go-i18n/v2 v2.6.0
1112
github.com/opencontainers/go-digest v1.0.0
1213
github.com/prometheus/client_golang v1.22.0
1314
github.com/siderolabs/gen v0.8.0
@@ -25,6 +26,7 @@ require (
2526
golang.org/x/net v0.39.0
2627
golang.org/x/sync v0.13.0
2728
golang.org/x/sys v0.32.0
29+
golang.org/x/text v0.24.0
2830
gopkg.in/yaml.v3 v3.0.1
2931
)
3032

@@ -255,7 +257,6 @@ require (
255257
golang.org/x/mod v0.24.0 // indirect
256258
golang.org/x/oauth2 v0.29.0 // indirect
257259
golang.org/x/term v0.31.0 // indirect
258-
golang.org/x/text v0.24.0 // indirect
259260
golang.org/x/time v0.11.0 // indirect
260261
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 // indirect
261262
google.golang.org/genproto/googleapis/api v0.0.0-20250409194420-de1ac958c67a // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo
7171
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
7272
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
7373
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
74+
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
75+
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
7476
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
7577
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
7678
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
@@ -542,6 +544,8 @@ github.com/mozillazg/docker-credential-acr-helper v0.4.0 h1:Uoh3Z9CcpEDnLiozDx+D
542544
github.com/mozillazg/docker-credential-acr-helper v0.4.0/go.mod h1:2kiicb3OlPytmlNC9XGkLvVC+f0qTiJw3f/mhmeeQBg=
543545
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
544546
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
547+
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
548+
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
545549
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
546550
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE=
547551
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw=

internal/frontend/http/css/output.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/frontend/http/http.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/google/go-containerregistry/pkg/name"
1818
"github.com/google/go-containerregistry/pkg/v1/remote"
1919
"github.com/julienschmidt/httprouter"
20+
"github.com/nicksnyder/go-i18n/v2/i18n"
2021
"github.com/siderolabs/gen/ensure"
2122
"github.com/siderolabs/gen/xerrors"
2223
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
@@ -144,6 +145,7 @@ func NewFrontend(
144145
registerRoute(frontend.router.GET, "/ui/version-doc", frontend.handleUIVersionDoc)
145146
registerRoute(frontend.router.POST, "/ui/extensions-list", frontend.handleUIExtensionsList)
146147
frontend.router.ServeFiles("/css/*filepath", http.FS(ensure.Value(fs.Sub(cssFS, "css"))))
148+
147149
frontend.router.ServeFiles("/favicons/*filepath", http.FS(ensure.Value(fs.Sub(faviconsFS, "favicons"))))
148150
frontend.router.ServeFiles("/js/*filepath", http.FS(ensure.Value(fs.Sub(jsFS, "js"))))
149151

@@ -178,3 +180,20 @@ func (f *Frontend) wrapper(h func(ctx context.Context, w http.ResponseWriter, r
178180
}
179181
}
180182
}
183+
184+
// Use several ways to detect language.
185+
func (f *Frontend) getLocalizer(r *http.Request) *i18n.Localizer {
186+
lang := r.URL.Query().Get("lang")
187+
188+
if lang == "" {
189+
if cookie, err := r.Cookie("lang"); err == nil {
190+
lang = cookie.Value
191+
}
192+
}
193+
194+
if lang == "" {
195+
lang = r.Header.Get("Accept-Language")
196+
}
197+
198+
return i18n.NewLocalizer(getLocalizerBundle(), lang, "en")
199+
}

internal/frontend/http/i18n.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
package http
6+
7+
import (
8+
"fmt"
9+
"path/filepath"
10+
11+
"github.com/nicksnyder/go-i18n/v2/i18n"
12+
"golang.org/x/text/language"
13+
"gopkg.in/yaml.v3"
14+
)
15+
16+
func loadLocalizerBundle() (*i18n.Bundle, error) {
17+
bundle := i18n.NewBundle(language.English)
18+
bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
19+
20+
entries, err := localesFS.ReadDir("locales")
21+
if err != nil {
22+
return nil, fmt.Errorf("failed to read locales directory: %w", err)
23+
}
24+
25+
for _, entry := range entries {
26+
if _, err := bundle.LoadMessageFileFS(localesFS, filepath.Join("locales", entry.Name())); err != nil {
27+
return nil, fmt.Errorf("failed to load translation file %s: %w", entry.Name(), err)
28+
}
29+
}
30+
31+
return bundle, nil
32+
}
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
- id: lang.name
2+
translation: Language
3+
4+
- id: lang.en
5+
translation: English
6+
7+
- id: lang.ru
8+
translation: Russian
9+
10+
- id: app.name
11+
translation: "Image Factory"
12+
13+
- id: distro.name
14+
translation: "Talos Linux"
15+
16+
- id: page.header
17+
translation: "Talos Linux Image Factory"
18+
19+
- id: info.developed_by
20+
translation: "The Talos Linux Image Factory, developed by "
21+
22+
- id: info.offers
23+
translation: ", offers a method to download various boot assets for "
24+
25+
- id: info.more
26+
translation: "For more information about the Image Factory API and the available image formats, please visit "
27+
28+
- id: info.github
29+
translation: "the GitHub repository"
30+
31+
- id: info.version
32+
translation: "Version"
33+
34+
- id: interface.loading
35+
translation: "Loading..."
36+
37+
- id: button.next
38+
translation: "Next"
39+
40+
- id: button.search
41+
translation: "Search"
42+
43+
- id: button.back
44+
translation: "Back"
45+
46+
- id: hardware.title
47+
translation: "Hardware Type"
48+
49+
- id: hardware.bmm.name
50+
translation: "Bare-metal Machine"
51+
52+
- id: hardware.cs.name
53+
translation: "Cloud Server"
54+
55+
- id: hardware.sbc.name
56+
translation: "Single Board Computer"
57+
58+
- id: hardware.bmm.description
59+
translation: "Suitable for x86-64 and arm64 bare-metal machines, as well as generic virtual machines. If unsure, choose this option."
60+
61+
- id: hardware.cs.description
62+
translation: "Compatible with AWS, GCP, Azure, VMWare, Equinix Metal, and other platforms, including homelab environments like Proxmox."
63+
64+
- id: hardware.sbc.description
65+
translation: "Supports Raspberry Pi, Pine64, Jetson Nano, and similar devices."
66+
67+
- id: versions.title
68+
translation: "Choose Talos Linux Version"
69+
70+
- id: versions.recommendation
71+
translation: "We strongly recommend using the latest stable version of Talos Linux"
72+
73+
- id: versions.recommendation.pre_release
74+
translation: "Pre-release versions are suitable for testing purposes but are not advised for production environments."
75+
76+
- id: docs.title
77+
translation: "Documentation for Talos Linux"
78+
79+
- id: docs.new
80+
translation: "What's New"
81+
82+
- id: docs.matrix
83+
translation: "Support Matrix"
84+
85+
- id: architecture.title
86+
translation: "Machine Architecture"
87+
88+
- id: architecture.amd64
89+
translation: "Compatible with Intel and AMD CPUs, also referred to as x86_64. If unsure, select this option."
90+
91+
- id: architecture.arm64
92+
translation: "Suitable for Ampere Computing and other arm64 CPUs. For Single Board Computers, choose the 'SBC' option on the first screen. For AWS and GCP arm64 VMs, use Cloud images."
93+
94+
- id: architecture.other
95+
translation: "Compatible with various ARM64 manufacturers, also known as aarch64."
96+
97+
- id: architecture.secure_boot.name
98+
translation: "SecureBoot"
99+
100+
- id: architecture.secure_boot.create
101+
translation: "Create a "
102+
103+
- id: architecture.secure_boot.description
104+
translation: " image signed with the official Sidero Labs signing key. This requires UEFI boot and pre-configured hardware."
105+
106+
- id: cloud.title
107+
translation: "Cloud"
108+
109+
- id: singleboard.title
110+
translation: "Single Board Computer"
111+
112+
- id: common.runs_on
113+
translation: "Runs on"
114+
115+
- id: common.documentation
116+
translation: "documentation"
117+
118+
- id: extensions.title
119+
translation: "System Extensions"
120+
121+
- id: extensions.description
122+
translation: " enhance the Talos Linux base image with additional features such as extra drivers, hardware firmware, container runtimes, guest agents, and more."
123+
124+
- id: extensions.select
125+
translation: "Select the extensions needed for your environment. If unsure, you can skip this step."
126+
127+
- id: customization.title
128+
translation: "Customization"
129+
130+
- id: customization.cmdline
131+
translation: "Extra kernel command line arguments:"
132+
133+
- id: customization.cmdline.description
134+
translation: "This step allows you to customize the default "
135+
136+
- id: customization.cmdline.arguments
137+
translation: "kernel command line arguments"
138+
139+
- id: customization.cmdline.example
140+
translation: "The syntax accepted is the same as the kernel command line syntax, e.g., "
141+
142+
- id: customization.cmdline.prefix
143+
translation: ". Prefixing an argument with "
144+
145+
- id: customization.cmdline.removes
146+
translation: " removes it from the default command line (e.g., "
147+
148+
- id: customization.cmdline.secure_boot
149+
translation: "With SecureBoot, the kernel command line is signed and cannot be modified, so this is the only opportunity to customize it."
150+
151+
- id: customization.cmdline.note
152+
translation: "Please note that kernel command line customization is only accepted for the initial boot images (ISO, PXE, disk image) and is ignored for "
153+
154+
- id: customization.cmdline.upgrade
155+
translation: " images. For upgrade/install command line customization, use the "
156+
157+
- id: customization.cmdline.configuration
158+
translation: " machine configuration field."
159+
160+
- id: customization.cmdline.skip
161+
translation: "Skip this step if unsure."
162+
163+
- id: customization.overlay
164+
translation: "Extra overlay options (advanced):"
165+
166+
- id: customization.overlay.description
167+
translation: "This step allows you to customize the overlay options for the "
168+
169+
- id: customization.overlay.name
170+
translation: "overlay"
171+
172+
- id: customization.overlay.syntax
173+
translation: "The accepted syntax is YAML (JSON) key-value pairs. The available options are specific to the overlay image and can be found in its documentation."
174+
175+
- id: customization.overlay.skip
176+
translation: "If unsure, you can skip this step."
177+
178+
- id: final.title
179+
translation: "Schematic Ready"
180+
181+
- id: final.image
182+
translation: "Your image schematic ID is: "
183+
184+
- id: final.first_boot
185+
translation: "First Boot"
186+
187+
- id: final.first_boot.metal
188+
translation: "Here are the options for the initial boot of Talos Linux on a bare-metal machine or a generic virtual machine:"
189+
190+
- id: final.first_boot.cloud
191+
translation: "Here are the options for the initial boot of Talos Linux on"
192+
193+
- id: final.first_boot.sbc
194+
translation: "Use the following disk image for"
195+
196+
- id: final.initial_installation
197+
translation: "Initial Installation"
198+
199+
- id: final.initial_installation.description
200+
translation: "For the initial installation of Talos Linux (not applicable for disk image boot), add the following installer image to the machine configuration:"
201+
202+
- id: final.upgrading
203+
translation: "Upgrading Talos Linux"
204+
205+
- id: final.upgrading.name
206+
translation: "upgrade"
207+
208+
- id: final.upgrading.to
209+
translation: "To "
210+
211+
- id: final.upgrading.description
212+
translation: " Talos Linux on the machine, use the following image:"
213+
214+
- id: final.documentation
215+
translation: "Documentation"
216+
217+
- id: final.documentation.new
218+
translation: "What's New in Talos "
219+
220+
- id: final.documentation.matrix
221+
translation: "Support Matrix for "
222+
223+
- id: final.documentation.getting_started
224+
translation: "Getting Started Guide"
225+
226+
- id: final.documentation.metal
227+
translation: "Bare-metal Network Configuration"
228+
229+
- id: final.documentation.cloud
230+
translation: "Guide"
231+
232+
- id: final.documentation.sbc
233+
translation: "Guide"
234+
235+
- id: final.documentation.secure_boot
236+
translation: "SecureBoot Guide"
237+
238+
- id: final.documentation.production
239+
translation: "Production Cluster Guide"
240+
241+
- id: final.documentation.troubleshooting
242+
translation: "Troubleshooting Guide"
243+
244+
- id: final.extra
245+
translation: "Extra Assets"
246+
247+
- id: final.extra.kernel_image
248+
translation: "Kernel Image"
249+
250+
- id: final.extra.cmdline
251+
translation: "Kernel Command Line"
252+
253+
- id: final.extra.initramfs
254+
translation: "Initramfs Image"
255+
256+
- id: final.disk_image.secure_boot
257+
translation: "SecureBoot Disk Image"
258+
259+
- id: final.disk_image.secure_boot_docs
260+
translation: "SecureBoot documentation"
261+
262+
- id: final.disk_image.iso_docs
263+
translation: "ISO documentation"
264+
265+
- id: final.disk_image.all
266+
translation: "Disk Image"
267+
268+
- id: final.pxe.secure_boot
269+
translation: "SecureBoot PXE"
270+
271+
- id: final.pxe.boot
272+
translation: "PXE boot"
273+
274+
- id: final.pxe.docs
275+
translation: "PXE documentation"
276+
277+
- id: final.board_image
278+
translation: "Disk Image"

0 commit comments

Comments
 (0)