WebThingsIO / gateway

WebThings Gateway - a self-hosted web application for monitoring and controlling a building over the web
http://webthings.io/gateway
Mozilla Public License 2.0
2.62k stars 339 forks source link

[Snap] Implement Localisation Settings #3153

Open benfrancis opened 3 months ago

benfrancis commented 3 months ago

STR:

Expected:

Actual:

From the browser console when setting Language:

Uncaught (in promise) Error: 500
    getJson api.ts:44
    promise callback*getJson api.ts:42
    getPushKey api.ts:474
    r notifications.js:23
    i thing-model.js:324
    a notifications.js:16
    promise callback*i thing-model.js:324
    a notifications.js:16
    s notifications.js:16
    s notifications.js:16
    onReady notifications.js:16
    promise callback*96710 app.js:619
    Webpack 3
api.ts:44:14

Also noticed network errors in the browser console during first time setup:

Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.

XHRPUT
http://localhost:8080/settings/localization/language
[HTTP/1.1 401 Unauthorized 1ms]

XHRGET
http://localhost:8080/settings/localization/units
[HTTP/1.1 401 Unauthorized 2ms]

Uncaught (in promise) Error: 401
    getJson http://localhost:8080/bundle/6f3f2b9653370ff453ac-app.js:8
    promise callback*getJson http://localhost:8080/bundle/6f3f2b9653370ff453ac-app.js:8
    getUnits http://localhost:8080/bundle/6f3f2b9653370ff453ac-app.js:8
    e http://localhost:8080/bundle/6f3f2b9653370ff453ac-app.js:8
    promise callback*e http://localhost:8080/bundle/6f3f2b9653370ff453ac-app.js:8
    96710 http://localhost:8080/bundle/6f3f2b9653370ff453ac-app.js:8
    n http://localhost:8080/bundle/6f3f2b9653370ff453ac-app.js:8
    <anonymous> http://localhost:8080/bundle/6f3f2b9653370ff453ac-app.js:8
    <anonymous> http://localhost:8080/bundle/6f3f2b9653370ff453ac-app.js:8
6f3f2b9653370ff453ac-app.js:8:946120

XHRGET
http://localhost:8080/settings/localization/language
[HTTP/1.1 401 Unauthorized 2ms]

On one occasion I also noticed an error in run-app.log after creating a username and password during first time setup:

2024-08-20 19:00:20.691 ERROR : Failed to set localization.language to 'en-GB'

dilyn-corner commented 1 month ago

Are there any errors or messages in the system log when this is attempted?

My initial guess is you need the locale-control interface.

You'll probably need to consider what locales to include in your snap(s) as well, though the core22 base snap does ship a usr/share/locale.

It may be worth investigating snaps like the gnome-**-2X04 snaps provide and how the gnome extension works. It may provide some insights into how you may need to craft your graphics setup to make everything work smoothly.

benfrancis commented 1 month ago

@dilyn-corner wrote:

It may be worth investigating snaps like the gnome-**-2X04 snaps provide and how the gnome extension works. It may provide some insights into how you may need to craft your graphics setup to make everything work smoothly.

There is no graphics setup since WebThings Gateway is a headless application, so I'm assuming this isn't relevant?

Looking more closely at the source code of what the Raspbian back end for localisation settings does I see:

The language and unit settings just seem to be stored internally to the application and are used for the Fluent localisation on the front end:

@dilyn-corner / @ogra1 What would be really useful to know is how you would implement all of the functions in that first list on Ubuntu Core and what interfaces would be required to do that. It looks like it really boils down to listing, getting and setting timezones and listing, getting and setting wireless country.

I see there is a timezone-control interface which allows setting the system timezone via systemd-timedated "independently of config core". Is this what I should use to list, get and set timezones and if so how?

I assume the reason for setting the Wi-Fi country is so that the correct (legally allowed) frequency bands are used for the country you are in. How would you do this on Ubuntu Core? Can it be set via NetworkManager, or do I need to use another tool? And how do I get a list of valid country codes?

Thanks

ogra1 commented 4 weeks ago

This is another place where the code misses proper abstraction to be more portable:

Looking more closely at the source code of what the Raspbian back end for localisation settings does I see:

* [`getValidTimezones()`](https://github.com/WebThingsIO/gateway/blob/master/src/platforms/linux-raspbian.ts#L805)  - This reads from `/usr/share/zoneinfo/zone.tab`

Instead of reading the file directly it should use timedatectl list-timezones

* [`getTimezone()`](https://github.com/WebThingsIO/gateway/blob/master/src/platforms/linux-raspbian.ts#L832) - This reads from `/etc/timezone`

Same thing timedatectl show| grep ^Timezone ...

* [`setTimezone()`](https://github.com/WebThingsIO/gateway/blob/master/src/platforms/linux-raspbian.ts#L854) - This calls `sudo raspi-config nonint do_change_timezone {zone}`

timedatectl set-timezone

* [`getValidWirelessCountries()`](https://github.com/WebThingsIO/gateway/blob/master/src/platforms/linux-raspbian.ts#L869) - This reads from `/usr/share/zoneinfo/iso3166.tab`

iw reg get

* [`getWirelessCountry()`](https://github.com/WebThingsIO/gateway/blob/master/src/platforms/linux-raspbian.ts#L896) - This callse `sudo raspi-config noint get_wifi_country` and reads from `/usr/share/zoneinfo/iso3166.tab`

Not sure what the difference is to the above function here ...

* [`setWirelessCountry()`](https://github.com/WebThingsIO/gateway/blob/master/src/platforms/linux-raspbian.ts#L938) - This reads from `/usr/share/zoneinfo/iso3166.tab` and calls `raspi-config nonint do_wifi_country {data}`. I'm guessing this may edit `wpa_supplicant.conf` on the back end but I don't know for sure.

iw reg set ...

All in all the code would really benefit from using the above standard tools, even without using it in a snap it would enable you to run seamlessly on fedora, arch, you name it ...

The language and unit settings just seem to be stored internally to the application and are used for the Fluent localisation on the front end:

* `Settings.getSetting('localization.language')`

* `Settings.setSetting('localization.language', {language})`

* `Settings.getSetting('localization.units.temperature')`

* `Settings.setSetting(`localization.units.{key}`, value)`

For all of these you likely need a proper locale-gen call ad ship the locale files your app wants to use which the steps that @dilyn-corner described above should provide.

benfrancis commented 4 weeks ago

This is another place where the code misses proper abstraction to be more portable

Just to be clear, the LinuxRaspbianPlatform class was always intended to be very specific to Raspbian, running on a Raspberry Pi. That's why there's an abstract BasePlatform class and then subclasses for different distributions.

Could it have been written in a more portable way? Yes, probably. But that wasn't even a consideration when some of this code was first written as a Raspberry Pi-only experiment as far back as 2017!

I'm grateful for your advice on how to implement this functionality in a more portable way, which will also work inside snap confinement. I'll work my way through your suggestions, thank you.