Skip to content

#2: i18n#9

Open
PaulineNemchak wants to merge 1 commit into
mainfrom
i18n
Open

#2: i18n#9
PaulineNemchak wants to merge 1 commit into
mainfrom
i18n

Conversation

@PaulineNemchak

@PaulineNemchak PaulineNemchak commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Closes #2

  • adds internationalisation, currently only for en (US) and en (GB) with GB as a default;
  • saves preferences to the DB;

@PaulineNemchak PaulineNemchak self-assigned this Apr 13, 2026
@PaulineNemchak PaulineNemchak added the enhancement New feature or request label Apr 13, 2026

@mrdimidium mrdimidium left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't understand why you're committing the generated code? Is there a reason it wasn't included in gitignore?

ALTER TABLE workers DISABLE ROW LEVEL SECURITY;
`)

migrator.AppendMigration("add_user_settings", `

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be more locale settings, it's more convenient to store them all in a single locale column with a custom type.

For languages, don't validate here: adding a locale will require migration.

Regarding the date/time format, don't store the format string. There are about three formats; store a simple enum here that specifies the type.

Fot timezone postgres already has validator: https://www.postgresql.org/docs/9.2/view-pg-timezone-names.html

Comment thread cmd/mirum-server/proto/api.proto Outdated
rpc WorkerList(WorkerListRequest) returns (WorkerListResponse);
rpc WorkerDelete(WorkerDeleteRequest) returns (WorkerDeleteResponse);

rpc UserSettingsUpdate(UserSettingsUpdateRequest) returns (UserSettingsUpdateResponse);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already have UserUpdate method

Comment thread cmd/mirum-server/web/i18n/en-US/index.json Outdated
Comment thread cmd/mirum-server/web/i18n/en-US/index.json
Comment thread cmd/mirum-server/web/i18n/load.ts Outdated
Comment thread cmd/mirum-server/web/lib/mount.tsx Outdated
@PaulineNemchak

Copy link
Copy Markdown
Contributor Author

I also don't understand why you're committing the generated code? Is there a reason it wasn't included in gitignore?

I thought it's needed

@mrdimidium

Copy link
Copy Markdown
Contributor

@PaulineNemchak, I would try to remove the generated files in gitignore (as we already do with proto).

@PaulineNemchak
PaulineNemchak force-pushed the i18n branch 6 times, most recently from 0ce0bf3 to b6f7e74 Compare April 15, 2026 21:42
Comment thread cmd/mirum-server/proto/api.proto Outdated
}];
}
message WorkerDeleteResponse {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to mess up the formatting

Comment thread cmd/mirum-server/proto/api.proto Outdated
optional string email = 2 [(buf.validate.field).string.email = true];
optional string password = 3 [(buf.validate.field).string.min_len = 1];
optional string locale = 4 [(buf.validate.field).string = {
in: ["en-GB", "en-US"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with db, the check here only gets in the way and requires updating the API when a language is added. It's just a string.

Comment thread Taskfile.yml Outdated
env:
CGO_ENABLED: "1" # required for -race
cmds:
- go test -race -count=1 ./...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it here?

Comment thread cmd/mirum-server/server_web.go Outdated
return
}

if locale := parseLocale(r.Header.Get("Accept-Language")); locale != "" {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly. You define a title for each page if the database doesn't have a value for it, rather than storing one upon login.

Comment thread cmd/mirum-server/server_web.go Outdated
func parseLocale(acceptLang string) string {
for _, part := range strings.Split(acceptLang, ",") {
tag := strings.TrimSpace(strings.SplitN(part, ";", 2)[0])
if strings.HasPrefix(tag, "en-US") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need a value parser (there may be several with weights), and not just a switch-case

Comment thread cmd/mirum-server/server_web.go Outdated
// For unauthenticated actors no DB call is made.
func localeFor(ctx context.Context, actor Actor, db *DB) string {
if actor.Kind() == KindUser {
if u, err := db.UserGet(ctx, actor, UserByID(actor.UserID())); err == nil && u.Locale != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need a separate request here, Actor is already a user, expand it.

@PaulineNemchak
PaulineNemchak force-pushed the i18n branch 10 times, most recently from d72f10a to 9b6eb63 Compare April 16, 2026 20:02
Comment thread Taskfile.yml Outdated
linux-ppc64le,
linux-loong64,
linux-s390x,
]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these changes for?

Comment thread cmd/mirum-server/proto/api.proto Outdated
optional string email = 2 [(buf.validate.field).string.email = true];
optional string password = 3 [(buf.validate.field).string.min_len = 1];
optional string locale = 4 [(buf.validate.field).string.max_len = 32];
optional string date_format = 5 [(buf.validate.field).string = {

@mrdimidium mrdimidium Apr 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not a string, but an enum. String validation also don't required in proto files

Comment thread cmd/mirum-server/proto/api.proto Outdated
bytes id = 1;
string email = 2;
google.protobuf.Timestamp created_at = 3;
optional string locale = 4;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like in a database locale should be an object

Comment thread cmd/mirum-server/database.go Outdated
// Invalidates all sessions when password changes.
func (db *DB) UserUpdate(ctx context.Context, actor Actor, ref UserRef, email *string, password *string, pepper []byte) error {
if email == nil && password == nil {
func (db *DB) UserUpdate(ctx context.Context, actor Actor, ref UserRef, email *string, password *string, pepper []byte, locale, dateFormat, timezone *string) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create an props object for aguments

Comment thread cmd/mirum-server/database.go Outdated
if password != nil {
if _, err := tx.Exec(ctx, `DELETE FROM sessions WHERE user_id = $1`, id); err != nil {
if locale != nil || dateFormat != nil || timezone != nil {
if _, err := tx.Exec(ctx,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more reliable prepare one sql request, use builder

Comment thread cmd/mirum-server/database.go Outdated

// UserSettingsInit sets locale on first login from browser hints.
// Only updates columns that are still NULL — never overwrites a user-chosen value.
func (db *DB) UserSettingsInit(ctx context.Context, actor Actor, id UserID, locale string) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't init user locale after login, use default from browser if it wasn't redefined in user settings

Comment thread cmd/mirum-server/server_web.go Outdated
return
}
data := map[string]any{
"user": map[string]string{"email": actor.Email()},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just save user object to "user", without flatten fields

DateFormat date_format = 2;
}

message User {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
message User {
message User {
bytes id = 1;
string email = 2;
google.protobuf.Timestamp created_at = 3;
optional Locale locale = 4;
optional string timezone = 5;
}

type DateFormat string

const (
DateFormatDMY DateFormat = "dmy"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change to numbers

// LocaleSettings holds a user's language and date-format preferences,
// stored as a composite column in the database.
type LocaleSettings struct {
Language *string

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they are not optional

type UserUpdateParams struct {
Email *string
Password *string
Pepper []byte

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user doesn't have pepper

CREATE TYPE locale_settings AS (
language TEXT,
date_format date_format_t,
timezone timezone_t

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rm

);
ALTER TABLE users ADD COLUMN locale locale_settings;
`, `
ALTER TABLE users DROP COLUMN locale;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add to existing table instead

`)

migrator.AppendMigration("add_user_settings", `
CREATE DOMAIN timezone_t AS TEXT

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too much

Comment thread cmd/mirum-server/database.go Outdated
// UserUpdate updates a user's email and/or password.
// UserUpdate updates a user's email, password, and/or preferences.
// Invalidates all sessions when password changes.
func (db *DB) UserUpdate(ctx context.Context, actor Actor, ref UserRef, email *string, password *string, pepper []byte) error {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returt pepper

// licenses serves /about/licenses. Public: no auth, no CSRF.
func (h *webHandler) licenses(w http.ResponseWriter, _ *http.Request) {
h.assets.renderPage(w, "licenses", http.StatusOK, licensesData)
func (h *webHandler) licenses(w http.ResponseWriter, r *http.Request) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write middleware instead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

i18n subsystem

2 participants